Please or Register to create posts and topics.

Help with sending data over WiFi and reading data

Page 1 of 2Next

Hi there,

I finally figured out how to send the data from the ArdEEG over TCP (because my access point receiver, my bionic prosthetic arm, is using TCP and so is my other transmitter). My problem is the way I send data is different from the example code on GitHub. Once you look at my code you'll understand exactly how I want my data sent. The problem with this is that I don't understand how to take "output" and convert it into meaningful values that I can store in variables before sending it off to my receiver using "client.print()". I'd like to be able to store the data in the following variables leftEar, rightEar, leftFrontalLobeFP1, rightFrontalLobeFP2, leftMotorCortexC3, rightMotorCortexC4, leftOccipitalLobeO1, rightOccipitalLobeO2, leftTemporalLobeT3, rightTemporalLobeT4. This will allow me to use a serial plotter on the receiving end and be able to manipulate the data so that I can close a fist. It would be nice to have an example code on GitHub for another arduino which demonstrates how we can output meaningful values after we receive the data.

Below is the code I have to transmit.

#include <WiFiS3.h>
#include <WiFiClient.h>
#include <Arduino.h>
#include <SPI.h>
#include "EMGFilters.h"
#define BRAIN_EEG_DEBUG true
#define BRAIN_EEG_FILTER_DEBUG true
// Discrete filters must work with fixed sample frequency
// EMG filter only supports "SAMPLE_FREQ_500HZ" or "SAMPLE_FREQ_1000HZ". Other EMGFilterSampleRate inputs will bypass the EMG_FILTER
#define BRAIN_EEG_FILTER_SAMPLE_RATE SAMPLE_FREQ_500HZ
const int brainEEGFilterSampleRate = 500;
// For countries where power transmission is at 50 Hz, change to "NOTCH_FREQ_50HZ"
// EMG filter only supports 50Hz and 60Hz input. Other inputs will bypass the EMG_FILTER
#define BRAIN_EEG_FILTER_HUM_FREQUENCY NOTCH_FREQ_60HZ // For countries where power transmission is at 60 Hz
const int brainEEGFilterHumFrequency = 60;
WiFiClient client;
EMGFilters brainEEGFilter;
char* ssid = "BIONIC_PROSTHETIC_ARM";
char* password = "RoboticsIsCool123";
const char* serverIP = "192.168.4.1";
const uint16_t serverPort = 80;
// Self-signed certificate for testing (replace with valid certificate for production)
const char* server_cert = R"EOF(
-----BEGIN CERTIFICATE-----
MIIC+TCCAeGgAwIBAgIJALIVf0/ZUHq9MA0GCSqGSIb3DQEBCwUAMCMxITAfBgNV
BAMTGG15ZXhhbXBsZS5leGFtcGxlLmNvbTAeFw0yMTAxMDEwMDAwMDBaFw0yMjAx
MDEwMDAwMDBaMCMxITAfBgNVBAMTGG15ZXhhbXBsZS5leGFtcGxlLmNvbTBcMA0G
CSqGSIb3DQEBAQUAA0sAMEgCQQDXN7dtF0lqTIgkS82ZkziKZ1X1Rq2c3BXzJZZD
Hj3OUqNq7bRH07S1+1cMB/N0+4iUdsiC7M8/j5sVWiZ/XJmZAgMBAAGjUDBOMB0G
A1UdDgQWBBTkKk1Z5R5z/1YB0Q0OlxAHdP3R8zAfBgNVHSMEGDAWgBTkKk1Z5R5z
/1YB0Q0OlxAHdP3R8zAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA0EAIBbM
aXZcAigb0xe5F/I0idQ6XitP/QFmQoyNf8K0/rOB3Lt3uukWbT8xFZ+6BUpEctGG
pD7HBsD8fv/JfO8PQf1K2w==
-----END CERTIFICATE-----
)EOF";
// Brain EEG Sensor Headcap and Electroencephalography EEG Filter Variables ///////////////////////////////////////
// Calibration:
// Put on the EEG headcap.
// Place one on the elbow as our reference signal, or anywhere else where there is little to no muscle.
// Place the other two spaced apart from each other by 2cm on the point of the muscle being measured where there is the most atrophy when flexing.
// Then release your muscles and wait a few seconds for the signal to normalize.
// Select the max value as the EMGThreshold. Any value under EMGThreshold will be set to zero
static int brainEEGFilterThreshold = 0;
int brainEEGSensorValue, brainEEGSensorValueFiltered, brainEEGSensorValueFilteredAmplified;
unsigned long brainEEGFilterTimeStamp;
unsigned long brainEEGFilterTimeBudget;
unsigned long brainEEGFilterTimeLeft;
byte leftEar, rightEar, leftFrontalLobeFP1, rightFrontalLobeFP2, leftMotorCortexC3, rightMotorCortexC4, leftOccipitalLobeO1, rightOccipitalLobeO2, leftTemporalLobeT3, rightTemporalLobeT4;
int a = 30;
int sampleCount = 0;
//ADC
//#define button_pin 7
const int button_pin = 7;
const int chip_select = 10; // Assuming SPI chip select pin
int test_DRDY = 5;
int button_state = 0;
const int size_of_data = 1350; //864;
byte output[size_of_data] = {};
void setup() {
// Wire.begin(SDA_PIN, SCL_PIN);
// Wire.setClock(500000); // 400kHz I2C clock. Comment this line if having compilation difficulties\
Serial.begin(115200);
// Initializing WiFi connection to bionic prosthetic arm
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Enabling WiFi on EMG Headcap...");
}
Serial.println("Succesfully Enabled WiFi on EMG Headcap!");
//ADC
pinMode(button_pin, INPUT); //initialize the led pin as output
pinMode(chip_select, OUTPUT);
digitalWrite(chip_select, LOW);
SPI.begin();
SPI.beginTransaction(SPISettings(600000, MSBFIRST, SPI_MODE1));
sendCommand(0x02); // wakeup
sendCommand(0x0A); // stop
sendCommand(0x06); // reset
sendCommand(0x11); // sdatac
// Write configurations
writeByte(0x01, 0x96);
writeByte(0x02, 0xD4);
writeByte(0x03, 0xFF);
writeByte(0x04, 0x00);
writeByte(0x0D, 0x00);
writeByte(0x0E, 0x00);
writeByte(0x0F, 0x00);
writeByte(0x10, 0x00);
writeByte(0x11, 0x00);
writeByte(0x15, 0x20);
writeByte(0x17, 0x00);
writeByte(0x05, 0x00);
writeByte(0x06, 0x00);
writeByte(0x07, 0x00);
writeByte(0x08, 0x00);
writeByte(0x09, 0x00);
writeByte(0x0A, 0x00);
writeByte(0x0B, 0x00);
writeByte(0x0C, 0x00);
writeByte(0x14, 0x80);
sendCommand(0x10);
sendCommand(0x08);
// Initializing the Electroencephalography EEG Filter /////////////////
brainEEGFilter.init(BRAIN_EEG_FILTER_SAMPLE_RATE, BRAIN_EEG_FILTER_HUM_FREQUENCY, true, true, true);
// Setup for time cost measure of the EEG Filter using micros()
// micros will overflow and auto return to zero every 70 minutes
brainEEGFilterTimeBudget = 1e6 / brainEEGFilterSampleRate;
//////////////////////////////////////////////////////////////////////
Serial.println("Reading events");
}
void loop() {
if (!client.connected()) {
Serial.println("Connecting to Bionic Prosthetic Arm Server...");
if (client.connect(serverIP, serverPort)) { // Connect to the server
Serial.println("Connected to Bionic Prosthetic Arms Server");
} else {
Serial.println("Connection to Bionic Prosthetic Arm failed.");
delay(1000);
return;
}
}
if (client.connected()) {
Serial.println("Client connected!");
// In order to meet the ADC sample frequency of the EEG filter,
// The time cost of the code in the loop should be measured at each iteration.
brainEEGFilterTimeStamp = micros();
for (int i = 0; i < 27; i++) {
output[sampleCount] = SPI.transfer(0xFF); // 0xFF is a dummy byte to trigger the read
sampleCount = sampleCount + 1;
}
if (sampleCount == size_of_data) {
Serial.println("Sample Count = Size of Data!");
for (int i = 0; i < size_of_data; i++) {
switch (i) {
case 0:
leftEar = output[i];
break;
case 1:
rightEar = output[i];
break;
case 2:
leftFrontalLobeFP1 = output[i];
break;
case 3:
rightFrontalLobeFP2 = output[i];
break;
case 4:
leftMotorCortexC3 = output[i];
break;
case 5:
rightMotorCortexC4 = output[i];
break;
case 6:
leftOccipitalLobeO1 = output[i];
break;
case 7:
rightOccipitalLobeO2 = output[i];
break;
case 8:
leftTemporalLobeT3 = output[i];
break;
case 9:
rightTemporalLobeT4 = output[i];
break;
}
if (BRAIN_EEG_DEBUG) {
Serial.print("Size of Data: "); Serial.println(size_of_data);
Serial.print("Left Ear: "); Serial.print(leftEar);
Serial.print("\tRight Ear: "); Serial.println(rightEar);
Serial.print("Left Frontal Lobe (FP1): "); Serial.print(leftFrontalLobeFP1);
Serial.print("\tRight Frontal Lobe (FP2): "); Serial.println(rightFrontalLobeFP2);
Serial.print("Left Motor Cortex (C3): "); Serial.print(leftMotorCortexC3);
Serial.print("\tRight Motor Cortex (C4): "); Serial.println(rightMotorCortexC4);
Serial.print("Left Occipital Lobe (O1): "); Serial.print(leftOccipitalLobeO1);
Serial.print("\tRight Occipital Lobe (O2): "); Serial.println(rightOccipitalLobeO2);
Serial.print("Left Temporal Lobe (T3): "); Serial.print(leftTemporalLobeT3);
Serial.print("\tRight Temporal Lobe (T4): "); Serial.println(rightTemporalLobeT4);
}
// Send data over WiFi
client.print("TX2,"); // Identifier to let the server know which device it's receiving from
Serial.print(size_of_data);
client.print(leftEar); client.print(",");
client.print(rightEar); client.print(",");
client.print(leftFrontalLobeFP1); client.print(",");
client.print(rightFrontalLobeFP2); client.print(",");
client.print(leftMotorCortexC3); client.print(",");
client.print(rightMotorCortexC4); client.print(",");
client.print(leftOccipitalLobeO1); client.print(",");
client.print(rightOccipitalLobeO2); client.print(",");
client.print(leftTemporalLobeT3); client.print(",");
client.print(rightTemporalLobeT4);
client.println();
//Serial.println("Data sent to Bionic Prosthetic Arm");
sampleCount = 0; // Reset the index after sending data
}
}
//delay(20);
}
}
void sendCommand(byte command) {
SPI.transfer(command);
}
void writeByte(byte registers, byte data) {
char spi_data = 0x40 | registers;
charspi_data_array[3];
spi_data_array[0] = spi_data;
spi_data_array[1] = 0x00;
spi_data_array[2] = data;
SPI.transfer(spi_data_array, 3);
}
PiEEG has reacted to this post.
PiEEG

Hi Alikhodrali, really nice to meet you and thank you for your detailed email.

To receive data via wi-fi I used this script for Windows
https://github.com/Ildaron/ardEEG/blob/main/software/python/1.Alpha_real_time.py

Does it make sense?
If you  would like to use Arduino to control some external objects probably be better not to send EEG data (if you don't use ML) and you can try signal processing and feature extraction directly in Arduino and send already via wifi only control signal for external object
I will answer your post in the forum a little later, that you so much for your post.

Hi there,

No problem!

I checked out your Python code for receiving and tried to convert it to arduino code as best as I could. I am now able to send a bunch of zero values to my bionic prosthetic arm. You are right that it would be better to do the processing directly on the Arduino. I have code below that works better to send data the way I want it over TCP. It is now based on your code for the Arduino and your Python code for Windows. The problem is that I'm still not sure how to manipulate the data before sending it. I tried, and so far, this is what I have in the code below, but I don't believe it works. I thought it did at one point because I was getting values between 0 and 255 but I couldn't reproduce it. In your arduino code, we send the "output" byte array over wifi in your Arduino code example, but how do we simply receive the values in variables such as "channel1" to "channel 8" or "leftEar, rightEar, leftFrontalLobeFP1, rightFrontalLobeFP2, leftMotorCortexC3, rightMotorCortexC4, leftOccipitalLobeO1, rightOccipitalLobeO2, leftTemporalLobeT3, rightTemporalLobeT4"? Could you help me modify my code to accomplish this? Or at least create code to do onboard processing on the Arduino?

Thanks!

#include <WiFiS3.h>
#include <WiFiClient.h>
#include <Arduino.h>
#include <SPI.h>
#include "EMGFilters.h"
#define BRAIN_EEG_DEBUG true
#define BRAIN_EEG_FILTER_DEBUG true
#define BRAIN_EEG_SERIAL_PLOTTER_DEBUG false
// Discrete filters must work with fixed sample frequency
// EMG filter only supports "SAMPLE_FREQ_500HZ" or "SAMPLE_FREQ_1000HZ". Other EMGFilterSampleRate inputs will bypass the EMG_FILTER
#define BRAIN_EEG_FILTER_SAMPLE_RATE SAMPLE_FREQ_500HZ
const int brainEEGFilterSampleRate = 500;
// For countries where power transmission is at 50 Hz, change to "NOTCH_FREQ_50HZ"
// EMG filter only supports 50Hz and 60Hz input. Other inputs will bypass the EMG_FILTER
#define BRAIN_EEG_FILTER_HUM_FREQUENCY NOTCH_FREQ_60HZ // For countries where power transmission is at 60 Hz
const int brainEEGFilterHumFrequency = 60;
WiFiClient client;
EMGFilters brainEEGFilter;
char* ssid = "BIONIC_PROSTHETIC_ARM";
char* password = "RoboticsIsCool123";
const char* serverIP = "192.168.4.1";
const uint16_t serverPort = 80;
// Self-signed certificate for testing (replace with valid certificate for production)
const char* server_cert = R"EOF(
-----BEGIN CERTIFICATE-----
MIIC+TCCAeGgAwIBAgIJALIVf0/ZUHq9MA0GCSqGSIb3DQEBCwUAMCMxITAfBgNV
BAMTGG15ZXhhbXBsZS5leGFtcGxlLmNvbTAeFw0yMTAxMDEwMDAwMDBaFw0yMjAx
MDEwMDAwMDBaMCMxITAfBgNVBAMTGG15ZXhhbXBsZS5leGFtcGxlLmNvbTBcMA0G
CSqGSIb3DQEBAQUAA0sAMEgCQQDXN7dtF0lqTIgkS82ZkziKZ1X1Rq2c3BXzJZZD
Hj3OUqNq7bRH07S1+1cMB/N0+4iUdsiC7M8/j5sVWiZ/XJmZAgMBAAGjUDBOMB0G
A1UdDgQWBBTkKk1Z5R5z/1YB0Q0OlxAHdP3R8zAfBgNVHSMEGDAWgBTkKk1Z5R5z
/1YB0Q0OlxAHdP3R8zAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA0EAIBbM
aXZcAigb0xe5F/I0idQ6XitP/QFmQoyNf8K0/rOB3Lt3uukWbT8xFZ+6BUpEctGG
pD7HBsD8fv/JfO8PQf1K2w==
-----END CERTIFICATE-----
)EOF";
// Brain EEG Sensor Headcap and Electroencephalography EEG Filter Variables ///////////////////////////////////////
// Calibration:
// Put on the EEG headcap.
// Place the two clips on your ears.
// Then think really hard and wait a few seconds for the signal to normalize.
// Select the max value as the brainEEGFilterThreshold. Any value under brainEEGFilterThreshold will be set to zero
static int brainEEGFilterThreshold = 0;
int brainEEGSensorValue, brainEEGSensorValueFiltered, brainEEGSensorValueFilteredAmplified;
unsigned long brainEEGFilterTimeStamp;
unsigned long brainEEGFilterTimeBudget;
unsigned long brainEEGFilterTimeLeft;
byte leftEar, rightEar, leftFrontalLobeFP1, rightFrontalLobeFP2, leftMotorCortexC3, rightMotorCortexC4, leftOccipitalLobeO1, rightOccipitalLobeO2, leftTemporalLobeT3, rightTemporalLobeT4;
// Constants
const int data_length = 1350; // Example length, adjust according to your needs
const int sample_length = 50;
float result[data_length] = {0};
float data_before_1[50] = {0};
float data_before_2[50] = {0};
float data_before_3[50] = {0};
float data_before_4[50] = {0};
float data_before_5[50] = {0};
float data_before_6[50] = {0};
float data_before_7[50] = {0};
float data_before_8[50] = {0};
float channel1[50] = {0};
float channel2[50] = {0};
float channel3[50] = {0};
float channel4[50] = {0};
float channel5[50] = {0};
float channel6[50] = {0};
float channel7[50] = {0};
float channel8[50] = {0};
float channel9[50] = {0};
uint32_t data_test = 0x7FFFFF;
uint32_t data_check = 0xFFFFFF;
uint32_t voltage_1 = 0;
uint32_t convert_voltage = 0;
uint32_t voltage_1_after_convert = 0;
int channel_num = 0;
int a = 30;
int sampleCount = 0;
//ADC
//#define button_pin 7
const int button_pin = 7;
const int chip_select = 10; // Assuming SPI chip select pin
int test_DRDY = 5;
int button_state = 0;
const int size_of_data = 1350; //864;
byte output[size_of_data] = {};
void setup() {
// Wire.begin(SDA_PIN, SCL_PIN);
// Wire.setClock(500000); // 400kHz I2C clock. Comment this line if having compilation difficulties\
Serial.begin(115200);
// Initializing WiFi connection to bionic prosthetic arm
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Enabling WiFi on EMG Headcap...");
}
Serial.println("Succesfully Enabled WiFi on EMG Headcap!");
//ADC
pinMode(button_pin, INPUT); //initialize the led pin as output
pinMode(chip_select, OUTPUT);
digitalWrite(chip_select, LOW);
SPI.begin();
SPI.beginTransaction(SPISettings(600000, MSBFIRST, SPI_MODE1));
sendCommand(0x02); // wakeup
sendCommand(0x0A); // stop
sendCommand(0x06); // reset
sendCommand(0x11); // sdatac
// Write configurations
writeByte(0x01, 0x96);
writeByte(0x02, 0xD4);
writeByte(0x03, 0xFF);
writeByte(0x04, 0x00);
writeByte(0x0D, 0x00);
writeByte(0x0E, 0x00);
writeByte(0x0F, 0x00);
writeByte(0x10, 0x00);
writeByte(0x11, 0x00);
writeByte(0x15, 0x20);
writeByte(0x17, 0x00);
writeByte(0x05, 0x00);
writeByte(0x06, 0x00);
writeByte(0x07, 0x00);
writeByte(0x08, 0x00);
writeByte(0x09, 0x00);
writeByte(0x0A, 0x00);
writeByte(0x0B, 0x00);
writeByte(0x0C, 0x00);
writeByte(0x14, 0x80);
sendCommand(0x10);
sendCommand(0x08);
// Initializing the Electroencephalography EEG Filter /////////////////
brainEEGFilter.init(BRAIN_EEG_FILTER_SAMPLE_RATE, BRAIN_EEG_FILTER_HUM_FREQUENCY, true, true, true);
// Setup for time cost measure of the EEG Filter using micros()
// micros will overflow and auto return to zero every 70 minutes
brainEEGFilterTimeBudget = 1e6 / brainEEGFilterSampleRate;
//////////////////////////////////////////////////////////////////////
Serial.println("Reading events");
}
void loop() {
if (!client.connected()) {
Serial.println("Connecting to Bionic Prosthetic Arm Server...");
if (client.connect(serverIP, serverPort)) { // Connect to the server
Serial.println("Connected to Bionic Prosthetic Arms Server");
} else {
Serial.println("Connection to Bionic Prosthetic Arm failed.");
delay(1000);
return;
}
}
if (client.connected()) {
Serial.println("Client connected!");
// In order to meet the ADC sample frequency of the EEG filter,
// The time cost of the code in the loop should be measured at each iteration.
brainEEGFilterTimeStamp = micros();
for (int i = 0; i < 27; i++) {
output[sampleCount] = SPI.transfer(0xFF); // 0xFF is a dummy byte to trigger the read
sampleCount = sampleCount + 1;
}
if (sampleCount == size_of_data) {
int dataLength = sizeof(output) / sizeof(output[0]);
// Create a new array to hold the individual bytes
byte dataList[dataLength];
// Populate the new array
for (int i = 0; i < dataLength; i++) {
dataList[i] = output[i];
}
}
for (int c = 0; c < size_of_data; c += 27) {
for (int a = 0; a < 26; a += 3) {
voltage_1 = (output[a + c] << 8) | output[a + 1 + c];
voltage_1 = (voltage_1 << 8) | output[a + 2 + c];
convert_voltage = voltage_1 | data_test;
if (convert_voltage == data_check) {
voltage_1_after_convert = 16777214 - voltage_1;
} else {
voltage_1_after_convert = voltage_1;
}
channel_num = a / 3;
result[channel_num] = roundToDecimal(1000000 * 4.5 * (voltage_1_after_convert / 16777215.0), 2);
}
channel1[c / 27] = result[1];
channel2[c / 27] = result[2];
channel3[c / 27] = result[3];
channel4[c / 27] = result[4];
channel5[c / 27] = result[5];
channel6[c / 27] = result[6];
channel7[c / 27] = result[7];
channel8[c / 27] = result[8];
if ((c / 27) == sample_length - 1) {
// Plot values to Serial Plotter
for (int i = 0; i < sample_length; i++) {
// Serial.print(channel1[i]);
// Serial.print("\t");
// Serial.print(channel2[i]);
// Serial.print("\t");
// Serial.print(channel3[i]);
// Serial.print("\t");
// Serial.print(channel4[i]);
// Serial.print("\t");
// Serial.print(channel5[i]);
// Serial.print("\t");
// Serial.print(channel6[i]);
// Serial.print("\t");
// Serial.print(channel7[i]);
// Serial.print("\t");
// Serial.print(channel8[i]);
// Serial.println();
leftFrontalLobeFP1 = channel1[i];
rightFrontalLobeFP2 = channel2[i];
leftMotorCortexC3 = channel3[i];
rightMotorCortexC4 = channel4[i];
leftOccipitalLobeO1 = channel5[i];
rightOccipitalLobeO2 = channel6[i];
leftTemporalLobeT3 = channel7[i];
rightTemporalLobeT4 = channel8[i];
if (BRAIN_EEG_DEBUG) {
Serial.print("Size of Data: "); Serial.println(size_of_data);
Serial.print("Left Ear: "); Serial.print(leftEar);
Serial.print("\tRight Ear: "); Serial.println(rightEar);
Serial.print("Left Frontal Lobe (FP1): "); Serial.print(leftFrontalLobeFP1);
Serial.print("\tRight Frontal Lobe (FP2): "); Serial.println(rightFrontalLobeFP2);
Serial.print("Left Motor Cortex (C3): "); Serial.print(leftMotorCortexC3);
Serial.print("\tRight Motor Cortex (C4): "); Serial.println(rightMotorCortexC4);
Serial.print("Left Occipital Lobe (O1): "); Serial.print(leftOccipitalLobeO1);
Serial.print("\tRight Occipital Lobe (O2): "); Serial.println(rightOccipitalLobeO2);
Serial.print("Left Temporal Lobe (T3): "); Serial.print(leftTemporalLobeT3);
Serial.print("\tRight Temporal Lobe (T4): "); Serial.println(rightTemporalLobeT4);
}
// Make sure to set all other debugs to false
if (BRAIN_EEG_SERIAL_PLOTTER_DEBUG) {
Serial.print(leftEar); Serial.print(" ");
Serial.print(rightEar); Serial.print(" ");
Serial.print(leftFrontalLobeFP1); Serial.print(" ");
Serial.print(rightFrontalLobeFP2); Serial.print(" ");
Serial.print(leftMotorCortexC3); Serial.print(" ");
Serial.print(rightMotorCortexC4); Serial.print(" ");
Serial.print(leftOccipitalLobeO1); Serial.print(" ");
Serial.print(rightOccipitalLobeO2); Serial.print(" ");
Serial.print(leftTemporalLobeT3); Serial.print(" ");
Serial.print(rightTemporalLobeT4); Serial.print(" ");
Serial.println();
}
// Send data over WiFi
client.print("TX2,"); // Identifier to let the server know which device it's receiving from
//client.print(leftEar); client.print(",");
//client.print(rightEar); client.print(",");
client.print(leftFrontalLobeFP1); client.print(",");
client.print(rightFrontalLobeFP2); client.print(",");
client.print(leftMotorCortexC3); client.print(",");
client.print(rightMotorCortexC4); client.print(",");
client.print(leftOccipitalLobeO1); client.print(",");
client.print(rightOccipitalLobeO2); client.print(",");
client.print(leftTemporalLobeT3); client.print(",");
client.print(rightTemporalLobeT4);
client.println();
}
// Reset arrays
memset(channel1, 0, sample_length * sizeof(float));
memset(channel2, 0, sample_length * sizeof(float));
memset(channel3, 0, sample_length * sizeof(float));
memset(channel4, 0, sample_length * sizeof(float));
memset(channel5, 0, sample_length * sizeof(float));
memset(channel6, 0, sample_length * sizeof(float));
memset(channel7, 0, sample_length * sizeof(float));
memset(channel8, 0, sample_length * sizeof(float));
//Serial.println("Data sent to Bionic Prosthetic Arm");
sampleCount = 0; // Reset the index after sending data
}
}
delay(20);
}
}
void sendCommand(byte command) {
SPI.transfer(command);
}
void writeByte(byte registers, byte data) {
char spi_data = 0x40 | registers;
charspi_data_array[3];
spi_data_array[0] = spi_data;
spi_data_array[1] = 0x00;
spi_data_array[2] = data;
SPI.transfer(spi_data_array, 3);
}
// Function to round a float to a specified number of decimal places
float roundToDecimal(float value, int decimalPlaces) {
float multiplier = pow(10.0, decimalPlaces);
returnround(value * multiplier) / multiplier;
}

Hi Alikhodrali,

You said that you getting values between 0 and 255, which is a good sign, you need to know to convert this digital value to voltage.

for a in range (0,26,3):
voltage_1=(output[a+c]<<8)| output[a+1+c]
voltage_1=(voltage_1<<8)| output[a+2+c]
convert_voktage=voltage_1|data_test

if convert_voktage==data_check:
voltage_1_after_convert=(16777214-voltage_1)
else:
voltage_1_after_convert=voltage_1

let me shortly explain how it works
1. ADS1299 it is 8 channel 24 bits analog-digital converter

2. It means for every channel we have 24 bits, so 3 bytes for one channel.

3. When we read the signal in ardEEG we convert the voltage to the digital value of 24 bits,  but we count these values ​​byte by byte

4. For one reading session ADS1299 reads 27 bytes, the first 3 bytes are status bytes, other 24 bytes it are 8 channels which we are sending from Arduino

5. Every channel has 3 bytes (1_bytes, 2_bytes, 3_bytes) so to convert them to voltage.

6. First you need to summarize them by shifting them to the left
Ch1 = 1_bytes << 2_bytes << 3_bytes ,
now you need to convert them to Voltage

but before that

need to make a test for a sign, ArdEEG has a bipolar voltage for ADC so it is means we should check the first bite of the reading data to understand if it is a negative measurement or positive

if Ch1 ==data_check:

Ch1 =(Ch1 -16777214)
else:
Ch1 =Ch1

and finally convert data to microvolts

"""
converts raw EEG data from a 24-bit ADC (ranging from 0 to 16777215) into microvolts by normalizing the data,
scaling it to a 4.5V reference voltage, and converting to microvolts,
then rounding to two decimal places. This transformation is necessary for accurately interpreting EEG signals, which are typically measured in microvolts
"""
eeg_data = round(1000000*4.5*(Ch1 /16777216),2) # 2 to the power of 24 = 16777216

in Arduino ADS1299 you can find more details about that from TI datasheet

 

Do you send data from Arduino to Arduino ? in my example I just send data to a laptop via wi-fi and with Python it is much easier to make manipulation with data

Hi,

Thank you so much for your help in explaining the code. I only need you to explain the following lines of code:

#ch1
data_after_1 = ch_1
dataset_1 = data_before_1 + data_before_1 + data_before_1 + data_after_1
data_before_1 = data_after_1

I'm not sure what data_after_1 is? Is it also an array? What about dataset_1 and data_before1? If they are arrays, then what size are they? Do they change size in the for loop?

Finally, I got the code working to output the values for all 8 channels on the Arduino Serial Plotter on my receiving end. I even ran through the EEG channels through an EMG filter. The problem is that there are random spikes occasionally even with the headcap not on my head. Is this normal behaviour? Here's a link to a video showing you what I mean: video.

The only thing I have left to do now is to finish programming the highpass and lowpass Butterworth filters, but I've been working non-stop on this all day and night, so I need some rest first.  Here's my current code below:

#include <WiFiS3.h>
#include <WiFiClient.h>
#include <Arduino.h>
#include <SPI.h>
#include "EMGFilters.h"
#include <vector>
#include <cmath>
#define BRAIN_EEG_DEBUG true
#define BRAIN_EEG_FILTER_DEBUG false
#define BRAIN_EEG_SERIAL_PLOTTER_DEBUG false
#define BRAIN_EEG_FILTER_ADVANCED true
// Discrete filters must work with fixed sample frequency
// EMG filter only supports "SAMPLE_FREQ_500HZ" or "SAMPLE_FREQ_1000HZ". Other EMGFilterSampleRate inputs will bypass the EMG_FILTER
#define BRAIN_EEG_FILTER_SAMPLE_RATE SAMPLE_FREQ_1000HZ
const int brainEEGFilterSampleRate = 500;
// For countries where power transmission is at 50 Hz, change to "NOTCH_FREQ_50HZ"
// EMG filter only supports 50Hz and 60Hz input. Other inputs will bypass the EMG_FILTER
#define BRAIN_EEG_FILTER_HUM_FREQUENCY NOTCH_FREQ_60HZ // For countries where power transmission is at 60 Hz
const int brainEEGFilterHumFrequency = 60;
char* ssid = "BIONIC_PROSTHETIC_ARM";
char* password = "RoboticsIsCool123";
const char* serverIP = "192.168.4.1";
const uint16_t serverPort = 80;
IPAddress localIP(192, 168, 1, 184);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);
// Self-signed certificate for testing (replace with valid certificate for production)
const char* server_cert = R"EOF(
-----BEGIN CERTIFICATE-----
MIIC+TCCAeGgAwIBAgIJALIVf0/ZUHq9MA0GCSqGSIb3DQEBCwUAMCMxITAfBgNV
BAMTGG15ZXhhbXBsZS5leGFtcGxlLmNvbTAeFw0yMTAxMDEwMDAwMDBaFw0yMjAx
MDEwMDAwMDBaMCMxITAfBgNVBAMTGG15ZXhhbXBsZS5leGFtcGxlLmNvbTBcMA0G
CSqGSIb3DQEBAQUAA0sAMEgCQQDXN7dtF0lqTIgkS82ZkziKZ1X1Rq2c3BXzJZZD
Hj3OUqNq7bRH07S1+1cMB/N0+4iUdsiC7M8/j5sVWiZ/XJmZAgMBAAGjUDBOMB0G
A1UdDgQWBBTkKk1Z5R5z/1YB0Q0OlxAHdP3R8zAfBgNVHSMEGDAWgBTkKk1Z5R5z
/1YB0Q0OlxAHdP3R8zAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA0EAIBbM
aXZcAigb0xe5F/I0idQ6XitP/QFmQoyNf8K0/rOB3Lt3uukWbT8xFZ+6BUpEctGG
pD7HBsD8fv/JfO8PQf1K2w==
-----END CERTIFICATE-----
)EOF";
// Brain EEG Sensor Headcap and Electroencephalography EEG Filter Variables ///////////////////////////////////////
// Calibration:
// Put on the EEG headcap.
// Place the two clips on your ears.
// Then think really hard and wait a few seconds for the signal to normalize.
// Select the max value as the brainEEGFilterThreshold. Any value under brainEEGFilterThreshold will be set to zero
static int brainEEGFilterThresholdChannel1 = 0;
static int brainEEGFilterThresholdChannel2 = 0;
static int brainEEGFilterThresholdChannel3 = 0;
static int brainEEGFilterThresholdChannel4 = 0;
static int brainEEGFilterThresholdChannel5 = 0;
static int brainEEGFilterThresholdChannel6 = 0;
static int brainEEGFilterThresholdChannel7 = 0;
static int brainEEGFilterThresholdChannel8 = 0;
long brainEEGSensorValueChannel1, brainEEGSensorValueFilteredChannel1, brainEEGSensorValueFilteredAmplifiedChannel1;
long brainEEGSensorValueChannel2, brainEEGSensorValueFilteredChannel2, brainEEGSensorValueFilteredAmplifiedChannel2;
long brainEEGSensorValueChannel3, brainEEGSensorValueFilteredChannel3, brainEEGSensorValueFilteredAmplifiedChannel3;
long brainEEGSensorValueChannel4, brainEEGSensorValueFilteredChannel4, brainEEGSensorValueFilteredAmplifiedChannel4;
long brainEEGSensorValueChannel5, brainEEGSensorValueFilteredChannel5, brainEEGSensorValueFilteredAmplifiedChannel5;
long brainEEGSensorValueChannel6, brainEEGSensorValueFilteredChannel6, brainEEGSensorValueFilteredAmplifiedChannel6;
long brainEEGSensorValueChannel7, brainEEGSensorValueFilteredChannel7, brainEEGSensorValueFilteredAmplifiedChannel7;
long brainEEGSensorValueChannel8, brainEEGSensorValueFilteredChannel8, brainEEGSensorValueFilteredAmplifiedChannel8;
float dataButterHighpassFilterChannel1[50] = {0};
float dataButterHighpassFilterChannel2[50] = {0};
float dataButterHighpassFilterChannel3[50] = {0};
float dataButterHighpassFilterChannel4[50] = {0};
float dataButterHighpassFilterChannel5[50] = {0};
float dataButterHighpassFilterChannel6[50] = {0};
float dataButterHighpassFilterChannel7[50] = {0};
float dataButterHighpassFilterChannel8[50] = {0};
float dataButterHighpassLowpassFilterChannel1[50] = {0};
float dataButterHighpassLowpassFilterChannel2[50] = {0};
float dataButterHighpassLowpassFilterChannel3[50] = {0};
float dataButterHighpassLowpassFilterChannel4[50] = {0};
float dataButterHighpassLowpassFilterChannel5[50] = {0};
float dataButterHighpassLowpassFilterChannel6[50] = {0};
float dataButterHighpassLowpassFilterChannel7[50] = {0};
float dataButterHighpassLowpassFilterChannel8[50] = {0};
unsigned long brainEEGFilterTimeStamp;
unsigned long brainEEGFilterTimeBudget;
unsigned long brainEEGFilterTimeLeft;
const int samplingFrequency = 250; // Hz
const double highpassCutoffFrequency = 8; // Cut-off frequency (-3 dB) (Hz)
const double lowpassCutoffFrequency = 12; // Cut-off frequency (-3 dB) (Hz)
const double normalizedHighpassCutoffFrequency = 2 * highpassCutoffFrequency / samplingFrequency; // Normalized cut-off frequency (Hz)
const double normalizedLowpassCutoffFrequency = 2 * lowpassCutoffFrequency / samplingFrequency; // Normalized cut-off frequency (Hz)
long leftEar, rightEar, leftFrontalLobeFP1, rightFrontalLobeFP2, leftMotorCortexC3, rightMotorCortexC4, leftOccipitalLobeO1, rightOccipitalLobeO2, leftTemporalLobeT3, rightTemporalLobeT4;
// Constants
const int chip_select = 10; // Assuming SPI chip select pin
const int numberOfChannels = 8; // Number of EEG Channels being read
const int bytesPerSample = 3; // Number of bytes per sample for each channel
const int samplesPerRead = 27; // Number of bytes read in each cycle
const int sampleSize = samplesPerRead / bytesPerSample; // Number of samples read per cycle
const int dataLength = 1350; //864
const int sampleLength = 50;
byte output[dataLength] = {}; // Array to store raw SPI data
long channelData[numberOfChannels][sampleSize]; // 2D array to store decoded data for each channel
long result[dataLength] = {0};
float dataBefore1[sampleLength] = {0};
float dataBefore2[sampleLength] = {0};
float dataBefore3[sampleLength] = {0};
float dataBefore4[sampleLength] = {0};
float dataBefore5[sampleLength] = {0};
float dataBefore6[sampleLength] = {0};
float dataBefore7[sampleLength] = {0};
float dataBefore8[sampleLength] = {0};
float dataAfter1[sampleLength] = {0};
float dataAfter2[sampleLength] = {0};
float dataAfter3[sampleLength] = {0};
float dataAfter4[sampleLength] = {0};
float dataAfter5[sampleLength] = {0};
float dataAfter6[sampleLength] = {0};
float dataAfter7[sampleLength] = {0};
float dataAfter8[sampleLength] = {0};
float datasetChannel1[sampleLength] = {0};
float datasetChannel2[sampleLength] = {0};
float datasetChannel3[sampleLength] = {0};
float datasetChannel4[sampleLength] = {0};
float datasetChannel5[sampleLength] = {0};
float datasetChannel6[sampleLength] = {0};
float datasetChannel7[sampleLength] = {0};
float datasetChannel8[sampleLength] = {0};
float datasetChannel9[sampleLength] = {0};
// std::vector<byte> channel1;
// std::vector<byte> channel2;
// std::vector<byte> channel3;
// std::vector<byte> channel4;
// std::vector<byte> channel5;
// std::vector<byte> channel6;
// std::vector<byte> channel7;
// std::vector<byte> channel8;
long channel1[sampleLength] = {0};
long channel2[sampleLength] = {0};
long channel3[sampleLength] = {0};
long channel4[sampleLength] = {0};
long channel5[sampleLength] = {0};
long channel6[sampleLength] = {0};
long channel7[sampleLength] = {0};
long channel8[sampleLength] = {0};
uint32_t dataTest = 0x7FFFFF;
uint32_t dataCheck = 0xFFFFFF;
uint32_t voltage = 0;
long voltageToBeConverted = 0;
long voltageConverted = 0;
long voltageConvertedScaledNormalized = 0;
int channelNumber = 0;
int a = 30;
int sampleCount = 0;
WiFiClient client;
EMGFilters brainEEGFilterChannel1;
EMGFilters brainEEGFilterChannel2;
EMGFilters brainEEGFilterChannel3;
EMGFilters brainEEGFilterChannel4;
EMGFilters brainEEGFilterChannel5;
EMGFilters brainEEGFilterChannel6;
EMGFilters brainEEGFilterChannel7;
EMGFilters brainEEGFilterChannel8;
struct ButterworthFilter {
floata[3];
floatb[3];
floatx[3];
floaty[3];
};
ButterworthFilter butterHighpass[8];
ButterworthFilter butterLowpass[8];
void setup() {
// Wire.begin(SDA_PIN, SCL_PIN);
// Wire.setClock(500000); // 400kHz I2C clock. Comment this line if having compilation difficulties\
Serial.begin(115200);
pinMode(chip_select, OUTPUT);
digitalWrite(chip_select, LOW);
SPI.begin();
SPI.beginTransaction(SPISettings(600000, MSBFIRST, SPI_MODE1));
sendCommand(0x02); // wakeup
sendCommand(0x0A); // stop
sendCommand(0x06); // reset
sendCommand(0x11); // sdatac
// Write configurations
writeByte(0x01, 0x96);
writeByte(0x02, 0xD4);
writeByte(0x03, 0xFF);
writeByte(0x04, 0x00);
writeByte(0x0D, 0x00);
writeByte(0x0E, 0x00);
writeByte(0x0F, 0x00);
writeByte(0x10, 0x00);
writeByte(0x11, 0x00);
writeByte(0x15, 0x20);
writeByte(0x17, 0x00);
writeByte(0x05, 0x00);
writeByte(0x06, 0x00);
writeByte(0x07, 0x00);
writeByte(0x08, 0x00);
writeByte(0x09, 0x00);
writeByte(0x0A, 0x00);
writeByte(0x0B, 0x00);
writeByte(0x0C, 0x00);
writeByte(0x14, 0x80);
sendCommand(0x10);
sendCommand(0x08);
// Initializing WiFi connection to bionic prosthetic arm
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Enabling WiFi on EMG Headcap...");
}
Serial.println("Succesfully Enabled WiFi on EMG Headcap!");
//WiFi.config(localIP, gateway, subnet);
// Initializing the Electroencephalography EEG Filter /////////////////
brainEEGFilterChannel1.init(BRAIN_EEG_FILTER_SAMPLE_RATE, BRAIN_EEG_FILTER_HUM_FREQUENCY, true, true, true);
brainEEGFilterChannel2.init(BRAIN_EEG_FILTER_SAMPLE_RATE, BRAIN_EEG_FILTER_HUM_FREQUENCY, true, true, true);
brainEEGFilterChannel3.init(BRAIN_EEG_FILTER_SAMPLE_RATE, BRAIN_EEG_FILTER_HUM_FREQUENCY, true, true, true);
brainEEGFilterChannel4.init(BRAIN_EEG_FILTER_SAMPLE_RATE, BRAIN_EEG_FILTER_HUM_FREQUENCY, true, true, true);
brainEEGFilterChannel5.init(BRAIN_EEG_FILTER_SAMPLE_RATE, BRAIN_EEG_FILTER_HUM_FREQUENCY, true, true, true);
brainEEGFilterChannel6.init(BRAIN_EEG_FILTER_SAMPLE_RATE, BRAIN_EEG_FILTER_HUM_FREQUENCY, true, true, true);
brainEEGFilterChannel7.init(BRAIN_EEG_FILTER_SAMPLE_RATE, BRAIN_EEG_FILTER_HUM_FREQUENCY, true, true, true);
brainEEGFilterChannel8.init(BRAIN_EEG_FILTER_SAMPLE_RATE, BRAIN_EEG_FILTER_HUM_FREQUENCY, true, true, true);
// Setup for time cost measure of the EEG Filter using micros()
// micros will overflow and auto return to zero every 70 minutes
brainEEGFilterTimeBudget = 1e6 / brainEEGFilterSampleRate;
//////////////////////////////////////////////////////////////////////
Serial.println("Reading events");
}
void loop() {
if (!client.connected()) {
Serial.println("Connecting to Bionic Prosthetic Arm Server...");
if (client.connect(serverIP, serverPort)) { // Connect to the server
Serial.println("Connected to Bionic Prosthetic Arm Server");
} else {
Serial.println("Connection to Bionic Prosthetic Arm failed.");
delay(1000);
return;
}
}
if (client.connected()) {
//Serial.println("Client connected!");
// In order to meet the ADC sample frequency of the EEG filter,
// The time cost of the code in the loop should be measured at each iteration.
brainEEGFilterTimeStamp = micros();
for (int i = 0; i < 27; i++) {
output[sampleCount] = SPI.transfer(0xFF); // 0xFF is a dummy byte to trigger the read
sampleCount = sampleCount + 1;
}
if (sampleCount == dataLength) {
Serial.println("sampleCount == dataLength");
for (int c = 0; c < dataLength; c += 27) {
for (int a = 0; a < 26; a += 3) {
voltage = (output[a + c] << 8) | output[a + 1 + c];
voltage = (voltage << 8) | output[a + 2 + c];
voltageToBeConverted = voltage | dataTest;
if (voltageToBeConverted == dataCheck) {
voltageConverted = 16777214 - voltage;
} else {
voltageConverted = voltage;
}
// Serial.print("Voltage: "); Serial.println(voltageConverted);
voltageConvertedScaledNormalized = round(1000000 * 4.5 * (voltageConverted / 16777215.0) * 100) / 100;
//Serial.print("Voltage (Scaled & Normalized): "); Serial.println(voltageConvertedScaledNormalized);
channelNumber = a / 3;
result[channelNumber] = voltageConvertedScaledNormalized;
}
int indexVal = c / 27;
//Serial.print("c / 27 = "); Serial.println(indexVal);
channel1[c / 27] = result[1];
channel2[c / 27] = result[2];
channel3[c / 27] = result[3];
channel4[c / 27] = result[4];
channel5[c / 27] = result[5];
channel6[c / 27] = result[6];
channel7[c / 27] = result[7];
channel8[c / 27] = result[8];
if ((c / 27) == sampleLength - 1) {
// Plot values to Serial Plotter
for (int i = 0; i < sampleLength; i++) {
if (BRAIN_EEG_FILTER_ADVANCED) {
// Getting Brain Electroencephalography EEG Sensor Values
brainEEGSensorValueChannel1 = channel1[i];
brainEEGSensorValueChannel2 = channel2[i];
brainEEGSensorValueChannel3 = channel3[i];
brainEEGSensorValueChannel4 = channel4[i];
brainEEGSensorValueChannel5 = channel5[i];
brainEEGSensorValueChannel6 = channel6[i];
brainEEGSensorValueChannel7 = channel7[i];
brainEEGSensorValueChannel8 = channel8[i];
// Brain Electroencephalography EEG filter processing /////////////////////////////////
brainEEGSensorValueFilteredChannel1 = brainEEGFilterChannel1.update(brainEEGSensorValueChannel1);
brainEEGSensorValueFilteredChannel2 = brainEEGFilterChannel2.update(brainEEGSensorValueChannel2);
brainEEGSensorValueFilteredChannel3 = brainEEGFilterChannel3.update(brainEEGSensorValueChannel3);
brainEEGSensorValueFilteredChannel4 = brainEEGFilterChannel4.update(brainEEGSensorValueChannel4);
brainEEGSensorValueFilteredChannel5 = brainEEGFilterChannel5.update(brainEEGSensorValueChannel5);
brainEEGSensorValueFilteredChannel6 = brainEEGFilterChannel6.update(brainEEGSensorValueChannel6);
brainEEGSensorValueFilteredChannel7 = brainEEGFilterChannel7.update(brainEEGSensorValueChannel7);
brainEEGSensorValueFilteredChannel8 = brainEEGFilterChannel8.update(brainEEGSensorValueChannel8);
// Square the EEG Filter Values
brainEEGSensorValueFilteredAmplifiedChannel1 = (brainEEGSensorValueFilteredChannel1);
brainEEGSensorValueFilteredAmplifiedChannel2 = (brainEEGSensorValueFilteredChannel2);
brainEEGSensorValueFilteredAmplifiedChannel3 = (brainEEGSensorValueFilteredChannel3);
brainEEGSensorValueFilteredAmplifiedChannel4 = (brainEEGSensorValueFilteredChannel4);
brainEEGSensorValueFilteredAmplifiedChannel5 = (brainEEGSensorValueFilteredChannel5);
brainEEGSensorValueFilteredAmplifiedChannel6 = (brainEEGSensorValueFilteredChannel6);
brainEEGSensorValueFilteredAmplifiedChannel7 = (brainEEGSensorValueFilteredChannel7);
brainEEGSensorValueFilteredAmplifiedChannel8 = (brainEEGSensorValueFilteredChannel8);
// Any value under brainEEGFilterThreshold will be set to zero
brainEEGSensorValueFilteredAmplifiedChannel1 = (brainEEGSensorValueFilteredAmplifiedChannel1 > brainEEGFilterThresholdChannel1) ? brainEEGSensorValueFilteredAmplifiedChannel1 : 0;
brainEEGSensorValueFilteredAmplifiedChannel2 = (brainEEGSensorValueFilteredAmplifiedChannel2 > brainEEGFilterThresholdChannel2) ? brainEEGSensorValueFilteredAmplifiedChannel2 : 0;
brainEEGSensorValueFilteredAmplifiedChannel3 = (brainEEGSensorValueFilteredAmplifiedChannel3 > brainEEGFilterThresholdChannel3) ? brainEEGSensorValueFilteredAmplifiedChannel3 : 0;
brainEEGSensorValueFilteredAmplifiedChannel4 = (brainEEGSensorValueFilteredAmplifiedChannel4 > brainEEGFilterThresholdChannel4) ? brainEEGSensorValueFilteredAmplifiedChannel4 : 0;
brainEEGSensorValueFilteredAmplifiedChannel5 = (brainEEGSensorValueFilteredAmplifiedChannel5 > brainEEGFilterThresholdChannel5) ? brainEEGSensorValueFilteredAmplifiedChannel5 : 0;
brainEEGSensorValueFilteredAmplifiedChannel6 = (brainEEGSensorValueFilteredAmplifiedChannel6 > brainEEGFilterThresholdChannel6) ? brainEEGSensorValueFilteredAmplifiedChannel6 : 0;
brainEEGSensorValueFilteredAmplifiedChannel7 = (brainEEGSensorValueFilteredAmplifiedChannel7 > brainEEGFilterThresholdChannel7) ? brainEEGSensorValueFilteredAmplifiedChannel7 : 0;
brainEEGSensorValueFilteredAmplifiedChannel8 = (brainEEGSensorValueFilteredAmplifiedChannel8 > brainEEGFilterThresholdChannel8) ? brainEEGSensorValueFilteredAmplifiedChannel8 : 0;
leftFrontalLobeFP1 = brainEEGSensorValueFilteredAmplifiedChannel1;
rightFrontalLobeFP2 = brainEEGSensorValueFilteredAmplifiedChannel2;
leftMotorCortexC3 = brainEEGSensorValueFilteredAmplifiedChannel3;
rightMotorCortexC4 = brainEEGSensorValueFilteredAmplifiedChannel4;
leftOccipitalLobeO1 = brainEEGSensorValueFilteredAmplifiedChannel5;
rightOccipitalLobeO2 = brainEEGSensorValueFilteredAmplifiedChannel6;
leftTemporalLobeT3 = brainEEGSensorValueFilteredAmplifiedChannel7;
rightTemporalLobeT4 = brainEEGSensorValueFilteredAmplifiedChannel8;
} else {
leftFrontalLobeFP1 = channel1[i];
rightFrontalLobeFP2 = channel2[i];
leftMotorCortexC3 = channel3[i];
rightMotorCortexC4 = channel4[i];
leftOccipitalLobeO1 = channel5[i];
rightOccipitalLobeO2 = channel6[i];
leftTemporalLobeT3 = channel7[i];
rightTemporalLobeT4 = channel8[i];
//////////////////////////////////////////////////////////////////
// dataAfter1[i] = channel1[i];
// datasetChannel1[i] = dataBefore1[i] + dataBefore1[i] + dataBefore1[i] + dataAfter1[i];
// dataBefore1[i] = dataAfter1[i];
// dataButterHighpassFilterChannel1[i] = butterHighpassFilter(datasetChannel1[i], highpassCutoffFrequency, samplingFrequency);
// dataButterHighpassLowpassFilterChannel1[i] = butterLowpassFilter(datasetChannel1[i], lowpassCutoffFrequency, samplingFrequency);
// dataAfter2[i] = channel2[i];
// datasetChannel2[i] = dataBefore2[i] + dataBefore2[i] + dataBefore2[i] + dataAfter2[i];
// dataBefore2[i] = dataAfter2[i];
// dataButterHighpassFilterChannel2[i] = butterHighpassFilter(datasetChannel2[i], highpassCutoffFrequency, samplingFrequency);
// dataButterHighpassLowpassFilterChannel2[i] = butterLowpassFilter(datasetChannel2[i], lowpassCutoffFrequency, samplingFrequency);
// dataAfter3[i] = channel3[i];
// datasetChannel3[i] = dataBefore3[i] + dataBefore3[i] + dataBefore3[i] + dataAfter3[i];
// dataBefore3[i] = dataAfter3[i];
// dataButterHighpassFilterChannel3[i] = butterHighpassFilter(datasetChannel3[i], highpassCutoffFrequency, samplingFrequency);
// dataButterHighpassLowpassFilterChannel3[i] = butterLowpassFilter(datasetChannel3[i], lowpassCutoffFrequency, samplingFrequency);
// dataAfter4[i] = channel4[i];
// datasetChannel4[i] = dataBefore4[i] + dataBefore4[i] + dataBefore4[i] + dataAfter4[i];
// dataBefore4[i] = dataAfter4[i];
// dataButterHighpassFilterChannel4[i] = butterHighpassFilter(datasetChannel4[i], highpassCutoffFrequency, samplingFrequency);
// dataButterHighpassLowpassFilterChannel4[i] = butterLowpassFilter(datasetChannel4[i], lowpassCutoffFrequency, samplingFrequency);
// dataAfter5[i] = channel5[i];
// datasetChannel5[i] = dataBefore5[i] + dataBefore5[i] + dataBefore5[i] + dataAfter5[i];
// dataBefore5[i] = dataAfter5[i];
// dataButterHighpassFilterChannel5[i] = butterHighpassFilter(datasetChannel5[i], highpassCutoffFrequency, samplingFrequency);
// dataButterHighpassLowpassFilterChannel5[i] = butterLowpassFilter(datasetChannel5[i], lowpassCutoffFrequency, samplingFrequency);
// dataAfter6[i] = channel6[i];
// datasetChannel6[i] = dataBefore6[i] + dataBefore6[i] + dataBefore6[i] + dataAfter6[i];
// dataBefore6[i] = dataAfter6[i];
// dataButterHighpassFilterChannel6[i] = butterHighpassFilter(datasetChannel6[i], highpassCutoffFrequency, samplingFrequency);
// dataButterHighpassLowpassFilterChannel6[i] = ButterLowpassFilter(datasetChannel6[i], lowpassCutoffFrequency, samplingFrequency);
// dataAfter7[i] = channel7[i];
// datasetChannel7[i] = dataBefore7[i] + dataBefore7[i] + dataBefore7[i] + dataAfter7[i];
// dataBefore7[i] = dataAfter7[i];
// dataButterHighpassFilterChannel7[i] = butterHighpassFilter(datasetChannel7[i], highpassCutoffFrequency, samplingFrequency);
// dataButterHighpassLowpassFilterChannel7[i] = butterLowpassFilter(datasetChannel7[i], lowpassCutoffFrequency, samplingFrequency);
// dataAfter8[i] = channel8[i];
// datasetChannel8[i] = dataBefore8[i] + dataBefore8[i] + dataBefore8[i] + dataAfter8[i];
// dataBefore8[i] = dataAfter8[i];
// dataButterHighpassFilterChannel8[i] = butterHighpassFilter(datasetChannel8[i], highpassCutoffFrequency, samplingFrequency);
// dataButterHighpassLowpassFilterChannel8[i] = butterLowpassFilter(datasetChannel8[i], lowpassCutoffFrequency, samplingFrequency);
// leftFrontalLobeFP1 = dataButterHighpassLowpassFilterChannel1[i];
// rightFrontalLobeFP2 = dataButterHighpassLowpassFilterChannel2[i];
// leftMotorCortexC3 = dataButterHighpassLowpassFilterChannel3[i];
// rightMotorCortexC4 = dataButterHighpassLowpassFilterChannel4[i];
// leftOccipitalLobeO1 = dataButterHighpassLowpassFilterChannel5[i];
// rightOccipitalLobeO2 = dataButterHighpassLowpassFilterChannel6[i];
// leftTemporalLobeT3 = dataButterHighpassLowpassFilterChannel7[i];
// rightTemporalLobeT4 = dataButterHighpassLowpassFilterChannel8[i];
}
//////////////////////////////////////////////////////////////////////
if (BRAIN_EEG_DEBUG) {
// Serial.print("Size of Data: "); Serial.println(dataLength);
// Serial.print("Left Ear: "); Serial.print(leftEar);
// Serial.print("\t\t\tRight Ear: "); Serial.println(rightEar);
Serial.print("Left Frontal Lobe (FP1): "); Serial.print(leftFrontalLobeFP1);
Serial.print("\tRight Frontal Lobe (FP2): "); Serial.println(rightFrontalLobeFP2);
Serial.print("Left Motor Cortex (C3): "); Serial.print(leftMotorCortexC3);
Serial.print("\tRight Motor Cortex (C4): "); Serial.println(rightMotorCortexC4);
Serial.print("Left Occipital Lobe (O1): "); Serial.print(leftOccipitalLobeO1);
Serial.print("\tRight Occipital Lobe (O2): "); Serial.println(rightOccipitalLobeO2);
Serial.print("Left Temporal Lobe (T3): "); Serial.print(leftTemporalLobeT3);
Serial.print("\tRight Temporal Lobe (T4): "); Serial.println(rightTemporalLobeT4);
Serial.print("\n");
}
if (BRAIN_EEG_SERIAL_PLOTTER_DEBUG) {
// Serial.print(leftEar); Serial.print(" ");
// Serial.print(rightEar); Serial.print(" ");
Serial.print(leftFrontalLobeFP1); Serial.print(" ");
Serial.print(rightFrontalLobeFP2); Serial.print(" ");
Serial.print(leftMotorCortexC3); Serial.print(" ");
Serial.print(rightMotorCortexC4); Serial.print(" ");
Serial.print(leftOccipitalLobeO1); Serial.print(" ");
Serial.print(rightOccipitalLobeO2); Serial.print(" ");
Serial.print(leftTemporalLobeT3); Serial.print(" ");
Serial.print(rightTemporalLobeT4); Serial.print(" ");
Serial.println();
}
// Send data over WiFi
client.print("TX2,"); // Identifier to let the server know which device it's receiving from
//client.print(leftEar); client.print(",");
//client.print(rightEar); client.print(",");
client.print(leftFrontalLobeFP1); client.print(",");
client.print(rightFrontalLobeFP2); client.print(",");
client.print(leftMotorCortexC3); client.print(",");
client.print(rightMotorCortexC4); client.print(",");
client.print(leftOccipitalLobeO1); client.print(",");
client.print(rightOccipitalLobeO2); client.print(",");
client.print(leftTemporalLobeT3); client.print(",");
client.print(rightTemporalLobeT4);
client.println();
// Brain Electroencephalography EEG Filter Timing Debug
// If time cost of the loop is less than brainEEGFilterTimeBudget, then you still have (brainEEGFilterTimeBudget - brainEEGFilterTimeStamp) for code execution.
// If time cost of the loop more than brainEEGFilterTimeBudget, then the sample rate needs to be reduced to SAMPLE_FREQ_500HZ
brainEEGFilterTimeStamp = micros() - brainEEGFilterTimeStamp;
brainEEGFilterTimeLeft = brainEEGFilterTimeBudget - brainEEGFilterTimeStamp;
if (BRAIN_EEG_FILTER_DEBUG) {
Serial.print("Brain Electroencephalography EEG Filters Time Cost: "); Serial.println(brainEEGFilterTimeStamp); // The filter costs an average of around 520 us
Serial.print("Time Left from EEG Filter Time Budget: "); Serial.println(brainEEGFilterTimeLeft);
Serial.print("\n");
} else {
//Serial.print("\n\n");
}
}
}
}
sampleCount = 0; // Reset the index after sending data
}
}
delay(40);
}
void sendCommand(byte command) {
SPI.transfer(command);
}
void writeByte(byte registers, byte data) {
char spi_data = 0x40 | registers;
charspi_data_array[3];
spi_data_array[0] = spi_data;
spi_data_array[1] = 0x00;
spi_data_array[2] = data;
SPI.transfer(spi_data_array, 3);
}
// Function to round a float to a specified number of decimal places
float roundToDecimal(float value, int decimalPlaces) {
float multiplier = pow(10.0, decimalPlaces);
returnround(value * multiplier) / multiplier;
}
void initButterworthFilters() {
for (int i = 0; i < 8; i++) {
// Initialize highpass filter coefficients
butterHighpass[i].a[0] = 1.0;
butterHighpass[i].a[1] = -1.995856;
butterHighpass[i].a[2] = 0.9960625;
butterHighpass[i].b[0] = 0.99803125;
butterHighpass[i].b[1] = -1.9960625;
butterHighpass[i].b[2] = 0.99803125;
// Initialize lowpass filter coefficients
butterLowpass[i].a[0] = 1.0;
butterLowpass[i].a[1] = -1.5610180758;
butterLowpass[i].a[2] = 0.6413515381;
butterLowpass[i].b[0] = 0.0200833656;
butterLowpass[i].b[1] = 0.0401667312;
butterLowpass[i].b[2] = 0.0200833656;
// Initialize filter states
for (int j = 0; j < 3; j++) {
butterHighpass[i].x[j] = 0.0;
butterHighpass[i].y[j] = 0.0;
butterLowpass[i].x[j] = 0.0;
butterLowpass[i].y[j] = 0.0;
}
}
}
float butterHighpassFilter(ButterworthFilter* filter, float input) {
// Shift old samples
filter->x[2] = filter->x[1];
filter->x[1] = filter->x[0];
filter->x[0] = input;
filter->y[2] = filter->y[1];
filter->y[1] = filter->y[0];
// Calculate new output
filter->y[0] = (filter->b[0] * filter->x[0] + filter->b[1] * filter->x[1] + filter->b[2] * filter->x[2]
- filter->a[1] * filter->y[1] - filter->a[2] * filter->y[2]) / filter->a[0];
returnfilter->y[0];
}
float butterLowpassFilter(ButterworthFilter* filter, float input) {
// Shift old samples
filter->x[2] = filter->x[1];
filter->x[1] = filter->x[0];
filter->x[0] = input;
filter->y[2] = filter->y[1];
filter->y[1] = filter->y[0];
// Calculate new output
filter->y[0] = (filter->b[0] * filter->x[0] + filter->b[1] * filter->x[1] + filter->b[2] * filter->x[2]
- filter->a[1] * filter->y[1] - filter->a[2] * filter->y[2]) / filter->a[0];
returnfilter->y[0];
}

Hi Alikhodrali

about that, it is attempt create let say buffer for data

#ch1
data_after_1 = ch_1
dataset_1 = data_before_1 + data_before_1 + data_before_1 + data_after_1
data_before_1 = data_after_1

when we work with band pass-filter in REAL-TIME we can not just only send our current data and make processing, because we need buffer, when a signal first enters a band-pass filter, the filter undergoes a transient response before reaching a steady state. So we send actual data and data from past reading to band pass filter after band-pass filter we take only actual data from the result

About your video, I just yesterday received the same question by email, it is copy

"
About data after the electrodes are disconnected, it is totally OK, you know, EEG is the potential difference between the reference (in our case the ear) and the electrode on the head, the potential difference as we know is voltage (for EEG microvolts), therefore whether the electrode is connected or not, the potential difference between the reference and the electrode channels will always be, in fact in this case, when electrode disconnected it is just noise and nothing more."

"

Do you have spikes during measurements ? when you don't move and have a good connection between the electrode and the skin

since in this case it may be a loss of data transmission, or the receiver does not have time to receive or the implemented script does not have time to send. To check this you can simply short-circuit the channels physically and check data, or short-circuit the channels through the registers
for the channel register, you need to send register 01. When channel in short-circuit stage you should receive stable data +- 2 µV

ch1set=0x05

write_byte (0x05, 0x01)

 

 

Quote from admin on 07.08.2024, 16:20

Hi Alikhodrali

about that, it is attempt create let say buffer for data

#ch1
data_after_1 = ch_1
dataset_1 = data_before_1 + data_before_1 + data_before_1 + data_after_1
data_before_1 = data_after_1

when we work with band pass-filter in REAL-TIME we can not just only send our current data and make processing, because we need buffer, when a signal first enters a band-pass filter, the filter undergoes a transient response before reaching a steady state. So we send actual data and data from past reading to band pass filter after band-pass filter we take only actual data from the result

I understand now thank you. So do you know what the structure of this data is? I'm assuming they're arrays but what would be their size?

with the current setup for registers ads1299 in ardEEG allows receiving 250 samples per sec from 8 channels

so every second  for every channel er receives 250 values (every value has 3 bytes)

So in my script, I collect data for 1 sec (250 values for every ch), and after that move forward

Yes, it arrays  dataset_1 has the next structure = [[250 values],[250 values],[250 values],[250 values]]

Do you have spikes during measurements ? when you don't move and have a good connection between the electrode and the skin

Yes, I do occasionally, maybe once every second or two. But what scale should my measurements be? Are my measurements supposed to be only positive values? Currently my min value in the y-axis is 0 and value when I see big spikes is about 1,000,000 to 2,000,000. Is this correct or is something wrong with the code somewhere?  When I put the cap on and clip on the ears, my values seem to go down, and once I remove the cap, the values go crazy as seen in the video here: Video. I'm just not sure about the scale. Does scale matter? Mind you the graph in the video is showing unfiltered raw data just converted to voltage.

since in this case it may be a loss of data transmission, or the receiver does not have time to receive or the implemented script does not have time to send. To check this you can simply short-circuit the channels physically and check data, or short-circuit the channels through the registers
for the channel register, you need to send register 01. When channel in short-circuit stage you should receive stable data +- 2 µV

 

ch1set=0x05

write_byte (0x05, 0x01)

When you say short circuit physically, do you mean put 5V to one of the electrodes? Sorry if this is a silly question. I've never done EEG before in my life.

with the current setup for registers ads1299 in ardEEG allows receiving 250 samples per sec from 8 channels

so every second  for every channel er receives 250 values (every value has 3 bytes)

So in my script, I collect data for 1 sec (250 values for every ch), and after that move forward

Yes, it arrays  dataset_1 has the next structure = [[250 values],[250 values],[250 values],[250 values]]

Also here are you saying that it is 250 samples per sec per channel as in 250/s * 8 = 2000 samples total? I hope I'm understanding this correctly? Which means that every second you are collecting a total of 2000*3 = 6000 bytes? So dataset_1 is an array of four arrays? I'll have to figure out how to do this in C++, I'm only familiar with this data structure in Python, Java and MATLAB.

Page 1 of 2Next