#include <FastLED.h>
#include <WiFi.h>
#include <WiFiUdp.h>
#include <OSCMessage.h>
#include <OSCBundle.h>
#include <OSCData.h>
#define HEATER_PIN 21 // ESP32 pin GPIO21 - heater control
#define NEOPIXEL_PIN 22 // ESP32 pin that connects to WS2812B
//#define ONBOARD_LED_PIN 8
#define NUM_LEDS 1 // The number of LEDs (pixels) on WS2812B
#define LED_TYPE WS2812B
#define COLOR_ORDER GRB
// MAX_MILLIWATTS can only be changed at compile-time. Use 0 to disable limit.
// Brightness can be changed at runtime via serial with 'b' and 'B'
#define MAX_MILLIWATTS 0
#define WIFI_SSID "Wokwi-GUEST"
#define WIFI_PASSWORD ""
// Defining the WiFi channel speeds up the connection:
#define WIFI_CHANNEL 6
#define LISTEN_PORT 5000
#define TIMEOUT_MILLIS 5000
#define SUMMARY_ELEMENT_NAME "muse_metrics28"
#define FILTER_COEF 0.05
#define FILTER_STEP 100
#define CALM_MAX_LEVEL 0.3;
#define PART1_MAX_LEVEL 200;
// 255-PART2_MAX_LEVEL
#define PART2_MAX_LEVEL 55;
#define MAX_CALM_PERIOD 120000;
uint8_t ledBrightness = 0;
uint8_t heaterLevel = 0;
unsigned long wifiInterval = 30000;
char* ssid = WIFI_SSID;
char* password = WIFI_PASSWORD;
uint8_t apChannel = WIFI_CHANNEL;
uint8_t wifiChannel = WIFI_CHANNEL;
char* hostname = "esp32-node-mind";
const char *passwordAP = "mind1234"; // Change this to your desired password
uint8_t counter = 0;
float calmMaxLevel = CALM_MAX_LEVEL;
float filterCoef = FILTER_COEF;
int filterStep = FILTER_STEP;
unsigned long filter_timer;
unsigned long calmTimer=0;
float curMindIndex = 20; // стартовый показатель делаем плохим (больше 1 - гасим лампочку)
long timeoutTargetMS = 0;
bool monitorOnline = false;
OSCErrorCode error;
WiFiUDP Udp;
CRGB leds[NUM_LEDS];
CRGB onb_led[1];
void setup() {
Serial.begin(115200);
Serial.println("Starting, ESP32-C6!");
redLED();
initHeater();
initLedStrip();
WiFi.mode(WIFI_AP_STA);
if (!initWifi()) {
initAP();
blueLED();
} else {
greenLED();
}
initOsc();
}
void loop() {
// counter++;
// Serial.println(counter);
receiveOscMessages(); // сообщения обновляются где-то раз в сек, но по 30 показателям. т.е. 30 сообщений в сек
updateLed();
updateHeater();
delay(100); // todo уменьшить, чтобы успеть выгрести поток
}
void receiveOscMessages() {
OSCBundle bundle;
int size = Udp.parsePacket();
if (size > 0) {
while (size--) {
bundle.fill(Udp.read());
}
if (!bundle.hasError()) {
monitorOnline = true;
bundle.dispatch(SUMMARY_ELEMENT_NAME, updateSummary);
} else {
error = bundle.getError();
Serial.print("error: ");
Serial.println(error);
}
//Reset timeout
timeoutTargetMS = millis() + TIMEOUT_MILLIS;
} else {
//Check for timeout
if(monitorOnline && millis()>timeoutTargetMS){
monitorOnline=false;
turnOffLED();
turnOffHeater();
}
}
}
void turnOffLED() {
leds[0] = CRGB::Black;
FastLED.show();
}
void turnOffHeater() {
analogWrite(HEATER_PIN, 0);
}
void updateSummary(OSCMessage &msg) {
if (millis() - filter_timer > filterStep) {
filter_timer = millis(); // просто таймер
curMindIndex = expRunningAverage(msg.getFloat(0));
Serial.print("avg OSC 28: ");
Serial.println(curMindIndex);
}
updateBrightness();
updateHeaterLevel();
}
void updateBrightness() {
// todo учитывать длительность нахождения в диапазоне, а не только curMindIndex
if (curMindIndex > calmMaxLevel) {
calmTimer = millis(); // сбросить счетчик времени покоя
}
if (curMindIndex > 1) {
ledBrightness = 0;
} else {
int part1 = (int) (1.0-curMindIndex)*PART1_MAX_LEVEL; // значение muse28
int part2 = 0; // показатель длительности периода непревышения порога расслабления
if (calmTimer > 0) {
unsigned long calmPeriod= (millis()-calmTimer);
if (calmPeriod > MAX_CALM_PERIOD) {
part2 = PART2_MAX_LEVEL;
} else {
part2 = (int) PART2_MAX_LEVEL*(MAX_CALM_PERIOD-calmPeriod)/MAX_CALM_PERIOD;
}
ledBrightness = part1 + part2;
}
}
}
void updateHeaterLevel() {
// todo
//heaterLevel = ledBrightness;
heaterLevel++;
}
// бегущее среднее (! одноразовая - использует статическую переменную. переделать в класс)
float expRunningAverage(float newVal) {
static float filVal = 0;
filVal += (newVal - filVal) * filterCoef;
return filVal;
}
// float getAveragePSD(OSCMessage &msg) {
// if (msg.size()==1) {
// return msg.getFloat(0); //Combined average can be sent by Muse Monitor
// } else {
// return (msg.getFloat(0)+msg.getFloat(1)+msg.getFloat(2)+msg.getFloat(3))/4.0; //TP9, AF7, AF8, TP10
// }
// }
void initOsc() {
Udp.begin(LISTEN_PORT);
Serial.print("Listening port: ");
Serial.println(LISTEN_PORT);
}
void initAP() {
Serial.println();
Serial.print("Starting access Point SSID: ");
Serial.println(hostname);
// Initialize ESP32 WiFi module in AP mode
//WiFi.softAPConfig(IPAddress(4, 3, 2, 1), IPAddress(4, 3, 2, 1), IPAddress(255, 255, 255, 0));
WiFi.softAP(hostname, passwordAP, apChannel);
Serial.print("IP address: ");
Serial.println(WiFi.softAPIP());
}
void greenLED() {
rgbLedWrite(RGB_BUILTIN, 0, RGB_BRIGHTNESS, 0); // onboard LED: Green
}
void blueLED() {
rgbLedWrite(RGB_BUILTIN, 0, 0, RGB_BRIGHTNESS); // onboard LED: Blue
}
void redLED() {
rgbLedWrite(RGB_BUILTIN, RGB_BRIGHTNESS, 0, 0); // onboard LED: Red
}
void WiFiStationConnected(WiFiEvent_t event, WiFiEventInfo_t info) {
Serial.println("Connected to AP successfully!");
//greenLED();
}
void printIP() {
Serial.println("WiFi connected");
// print the SSID of the network you're attached to:
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
Serial.print("ESP32 HostName: ");
Serial.println(WiFi.getHostname());
// print the received signal strength:
Serial.print("RSSI : ");
Serial.println(WiFi.RSSI());
// print your MAC address:
byte mac[6];
WiFi.macAddress(mac);
Serial.print("MAC address: ");
Serial.print(mac[5], HEX);
Serial.print(":");
Serial.print(mac[4], HEX);
Serial.print(":");
Serial.print(mac[3], HEX);
Serial.print(":");
Serial.print(mac[2], HEX);
Serial.print(":");
Serial.print(mac[1], HEX);
Serial.print(":");
Serial.println(mac[0], HEX);
}
void WiFiGotIP(WiFiEvent_t event, WiFiEventInfo_t info) {
printIP();
//greenLED();
}
void WiFiStationDisconnected(WiFiEvent_t event, WiFiEventInfo_t info) {
// todo проверить режим WIFI_STA
Serial.println("Disconnected from WiFi access point");
Serial.print("WiFi lost connection. Reason: ");
Serial.println(info.wifi_sta_disconnected.reason);
redLED();
Serial.print("Trying to Reconnect to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
}
boolean initWifi() {
WiFi.disconnect();
//WiFi.mode(WIFI_STA);
delay(100);
WiFi.setHostname(hostname);
WiFi.onEvent(WiFiStationConnected, WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_CONNECTED);
WiFi.onEvent(WiFiGotIP, WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_GOT_IP);
WiFi.onEvent(WiFiStationDisconnected, WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_DISCONNECTED);
WiFi.begin(ssid, password, wifiChannel);
//WiFi.config(ip, gateway, subnet);
Serial.print("Connecting to WiFi ");
Serial.println(ssid);
// Wait for connection
unsigned long currentMillis = millis();
unsigned long wifiPreviousMillis = 0;
while (WiFi.status() != WL_CONNECTED && (currentMillis - wifiPreviousMillis < wifiInterval)) {
// todo doBlinking();
wifiPreviousMillis = currentMillis;
delay(100);
}
return (WiFi.status() == WL_CONNECTED);
}
void initLedStrip() {
FastLED.addLeds<LED_TYPE, NEOPIXEL_PIN, COLOR_ORDER>(leds, NUM_LEDS); //.setCorrection(TypicalLEDStrip);
FastLED.setBrightness(ledBrightness);
if (MAX_MILLIWATTS > 0) FastLED.setMaxPowerInMilliWatts(MAX_MILLIWATTS);
}
void initHeater() {
pinMode(HEATER_PIN, OUTPUT);
}
void updateLed() {
leds[0] = CRGB::Blue;
FastLED.setBrightness(ledBrightness);
FastLED.show();
}
void updateHeater() {
// м.б. при уровне меньше какого-то порога надо ставить 0, чтобы нагреватель не включался совсем
analogWrite(HEATER_PIN, heaterLevel);
}
Loading
esp32-c6-devkitc-1
esp32-c6-devkitc-1