Merge branch 'main' of https://git.znachor.cz/BigTire/Neurosky-Mindwave-M5Stack-Core2-connection
|
@ -1,393 +0,0 @@
|
||||||
#define BAUDRATE 115200
|
|
||||||
#define DEBUGOUTPUT 0
|
|
||||||
#include <map> // Bluetooth
|
|
||||||
#include <BluetoothSerial.h> // Bluetooth
|
|
||||||
#include <M5Core2.h>
|
|
||||||
#include "icons.cpp" // icons
|
|
||||||
|
|
||||||
// UART communication variables
|
|
||||||
#define RX_PIN 19
|
|
||||||
#define TX_PIN 27
|
|
||||||
String stringReceived = "";
|
|
||||||
int valueReceived = 0;
|
|
||||||
|
|
||||||
// GUI variables
|
|
||||||
int brightnessValue = 1;
|
|
||||||
ButtonColors noDraw = {NODRAW, NODRAW, NODRAW};
|
|
||||||
Button screenModeButton(0, 190, 50, 50, false, "", noDraw);
|
|
||||||
Button brightnessButton(55, 190, 50, 50, false, "", noDraw);
|
|
||||||
Button restartButton(200, 191, 45, 50, false, "", noDraw);
|
|
||||||
Button powerOffButton(260, 186, 55, 55, false, "", noDraw);
|
|
||||||
|
|
||||||
enum screenMode {DARK, LIGHT};
|
|
||||||
screenMode COLOR_MODE = LIGHT;
|
|
||||||
|
|
||||||
uint16_t COLORS[2] = {WHITE, BLACK};
|
|
||||||
|
|
||||||
// Bluetooth Serial, variables
|
|
||||||
BluetoothSerial SerialBT;
|
|
||||||
#define BT_DISCOVER_TIME 10000
|
|
||||||
esp_spp_sec_t sec_mask=ESP_SPP_SEC_NONE;
|
|
||||||
esp_spp_role_t role=ESP_SPP_ROLE_SLAVE;
|
|
||||||
boolean isConnected = false;
|
|
||||||
|
|
||||||
// checksum variables
|
|
||||||
byte generatedChecksum = 0;
|
|
||||||
byte checksum = 0;
|
|
||||||
int payloadLength = 0;
|
|
||||||
byte payloadData[64] = {0};
|
|
||||||
byte poorQuality = 200;
|
|
||||||
byte attention = 0;
|
|
||||||
byte meditation = 0;
|
|
||||||
short raw;
|
|
||||||
uint32_t eegPower[8];
|
|
||||||
|
|
||||||
// system variables
|
|
||||||
long lastReceivedPacket = 0;
|
|
||||||
boolean bigPacket = false;
|
|
||||||
|
|
||||||
//////////////////////////
|
|
||||||
// Microprocessor Setup //
|
|
||||||
//////////////////////////
|
|
||||||
|
|
||||||
void setup(){
|
|
||||||
Serial.begin(BAUDRATE);
|
|
||||||
Serial2.begin(9600, SERIAL_8N1, RX_PIN, TX_PIN); // UART communication
|
|
||||||
M5.begin();
|
|
||||||
M5.Axp.SetLcdVoltage(3150); // Set initial brightness
|
|
||||||
|
|
||||||
// GUI
|
|
||||||
screenModeButton.addHandler(screenModeHandler, E_TOUCH);
|
|
||||||
brightnessButton.addHandler(brightnessHandler, E_TOUCH);
|
|
||||||
restartButton.addHandler(restartHandler, E_TOUCH);
|
|
||||||
powerOffButton.addHandler(powerOffHandler, E_TOUCH);
|
|
||||||
drawGui(COLOR_MODE);
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------------------------------------------------------
|
|
||||||
// ----------------------------------------------------------------------------------------------------------------------------------
|
|
||||||
// ----------------------------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
//////////////////////////
|
|
||||||
// BLUETOOTH CONNECTION //
|
|
||||||
//////////////////////////
|
|
||||||
|
|
||||||
if(! SerialBT.begin("ESP32test", true) ) {
|
|
||||||
Serial.println("========== serialBT failed!");
|
|
||||||
}
|
|
||||||
|
|
||||||
Serial.println("Starting discoverAsync...");
|
|
||||||
BTScanResults* btDeviceList = SerialBT.getScanResults();
|
|
||||||
if (SerialBT.discoverAsync([](BTAdvertisedDevice* pDevice) {
|
|
||||||
Serial.printf(">>>>>>>>>>>Found a new device asynchronously: %s\n", pDevice->toString().c_str());
|
|
||||||
} )
|
|
||||||
) {
|
|
||||||
delay(BT_DISCOVER_TIME);
|
|
||||||
Serial.print("Stopping discoverAsync... ");
|
|
||||||
SerialBT.discoverAsyncStop();
|
|
||||||
Serial.println("discoverAsync stopped");
|
|
||||||
delay(5000);
|
|
||||||
if(btDeviceList->getCount() > 0) {
|
|
||||||
BTAddress addr;
|
|
||||||
int channel=0;
|
|
||||||
Serial.println("Found devices:");
|
|
||||||
for (int i=0; i < btDeviceList->getCount(); i++) {
|
|
||||||
BTAdvertisedDevice *device=btDeviceList->getDevice(i);
|
|
||||||
Serial.printf(" ----- %s %s %d\n", device->getAddress().toString().c_str(), device->getName().c_str(), device->getRSSI());
|
|
||||||
std::map<int,std::string> channels=SerialBT.getChannels(device->getAddress());
|
|
||||||
Serial.printf("scanned for services, found %d\n", channels.size());
|
|
||||||
for(auto const &entry : channels) {
|
|
||||||
Serial.printf(" channel %d (%s)\n", entry.first, entry.second.c_str());
|
|
||||||
}
|
|
||||||
if(channels.size() > 0) {
|
|
||||||
addr = device->getAddress();
|
|
||||||
channel=channels.begin()->first;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(addr) {
|
|
||||||
Serial.printf("connecting to %s - %d\n", addr.toString().c_str(), channel);
|
|
||||||
SerialBT.connect(addr, channel, sec_mask, role);
|
|
||||||
isConnected = true;
|
|
||||||
drawGui(COLOR_MODE);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Serial.println("Didn't find any devices");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Serial.println("Error on discoverAsync f.e. not workin after a \"connect\"");
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------------------------------------------------------
|
|
||||||
// ----------------------------------------------------------------------------------------------------------------------------------
|
|
||||||
// ----------------------------------------------------------------------------------------------------------------------------------
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////////////////////
|
|
||||||
// Read data from Serial Bluetooth //
|
|
||||||
/////////////////////////////////////
|
|
||||||
|
|
||||||
byte ReadOneByte() {
|
|
||||||
int ByteRead;
|
|
||||||
if(! SerialBT.isClosed() && SerialBT.connected()) {
|
|
||||||
ByteRead = SerialBT.read();
|
|
||||||
|
|
||||||
#if DEBUGOUTPUT
|
|
||||||
Serial.print((char)ByteRead); // echo the same byte out the USB serial (for debug purposes)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return ByteRead;
|
|
||||||
} else {
|
|
||||||
Serial.println("not connected");
|
|
||||||
M5.update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////
|
|
||||||
//MAIN LOOP//
|
|
||||||
/////////////
|
|
||||||
void loop() {
|
|
||||||
M5.update();
|
|
||||||
if (!SerialBT.connected() && isConnected == true) {
|
|
||||||
isConnected = false;
|
|
||||||
drawGui(COLOR_MODE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// UART communication
|
|
||||||
if(Serial2.available()){
|
|
||||||
char c = Serial2.read();
|
|
||||||
stringReceived += c;
|
|
||||||
if (stringReceived.length() == 1) {
|
|
||||||
valueReceived = stringReceived.toInt();
|
|
||||||
stringReceived = "";
|
|
||||||
} else {
|
|
||||||
stringReceived = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(! SerialBT.isClosed() && SerialBT.connected()) {
|
|
||||||
if(ReadOneByte() == 170) {
|
|
||||||
if(ReadOneByte() == 170) {
|
|
||||||
payloadLength = ReadOneByte();
|
|
||||||
if(payloadLength > 169) //Payload length can not be greater than 169
|
|
||||||
return;
|
|
||||||
|
|
||||||
generatedChecksum = 0;
|
|
||||||
|
|
||||||
for(int i = 0; i < payloadLength; i++) {
|
|
||||||
payloadData[i] = ReadOneByte(); //Read payload into memory
|
|
||||||
generatedChecksum += payloadData[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
checksum = ReadOneByte(); //Read checksum byte from stream
|
|
||||||
generatedChecksum = 255 - generatedChecksum; //Take one's compliment of generated checksum
|
|
||||||
|
|
||||||
if(checksum == generatedChecksum) {
|
|
||||||
|
|
||||||
for(int i = 0; i < payloadLength; i++) { // Parse the payload
|
|
||||||
switch (payloadData[i]) {
|
|
||||||
case 2:
|
|
||||||
i++;
|
|
||||||
poorQuality = payloadData[i];
|
|
||||||
bigPacket = true;
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
i++;
|
|
||||||
attention = payloadData[i];
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
i++;
|
|
||||||
meditation = payloadData[i];
|
|
||||||
break;
|
|
||||||
case 0x80:
|
|
||||||
i++;
|
|
||||||
raw = (payloadData[i++]<<8) | payloadData[i++];
|
|
||||||
break;
|
|
||||||
case 0x83:
|
|
||||||
for (int j = 0; j < 8; j++) {
|
|
||||||
uint8_t a,b,c;
|
|
||||||
a = payloadData[++i];
|
|
||||||
b = payloadData[++i];
|
|
||||||
c = payloadData[++i];
|
|
||||||
eegPower[j] = ((uint32_t)a << 16) | ((uint32_t)b << 8) | (uint32_t)c;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
} // switch
|
|
||||||
} // for loop
|
|
||||||
|
|
||||||
#if !DEBUGOUTPUT
|
|
||||||
if(bigPacket) {
|
|
||||||
drawGui(COLOR_MODE);
|
|
||||||
|
|
||||||
// UART OUTPUT
|
|
||||||
switch (valueReceived) {
|
|
||||||
case 0:
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
Serial2.print(attention);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
Serial2.print(meditation);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
Serial2.print(poorQuality);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// SERIAL OUTPUT
|
|
||||||
Serial.print("Attention: ");
|
|
||||||
Serial.print(attention);
|
|
||||||
Serial.print("\n");
|
|
||||||
Serial.print("Poor quality: ");
|
|
||||||
Serial.print(poorQuality);
|
|
||||||
Serial.print("\n");
|
|
||||||
Serial.print("Meditation: ");
|
|
||||||
Serial.print(meditation);
|
|
||||||
Serial.print("\n");
|
|
||||||
Serial.print("Raw: ");
|
|
||||||
Serial.print(raw);
|
|
||||||
Serial.print("\n");
|
|
||||||
Serial.print("EEG POWER: ");
|
|
||||||
Serial.print(String(eegPower[0]) + ", " + String(eegPower[1]) + ", " + String(eegPower[2]) + ", " + String(eegPower[3]) + ", " + String(eegPower[4]) + ", " + String(eegPower[5]) + ", " + String(eegPower[6]) + ", " + String(eegPower[7]));
|
|
||||||
Serial.print("\n");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
bigPacket = false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Checksum Error
|
|
||||||
} // end if else for checksum
|
|
||||||
} // end if read 0xAA byte
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------------------------------------------------------
|
|
||||||
// ----------------------------------------------------------------------------------------------------------------------------------
|
|
||||||
// ----------------------------------------------------------------------------------------------------------------------------------
|
|
||||||
// GUI
|
|
||||||
|
|
||||||
void drawGui(screenMode COLOR_MODE) {
|
|
||||||
M5.Lcd.clear();
|
|
||||||
M5.Lcd.fillScreen((COLORS[COLOR_MODE] - 1) % 2);
|
|
||||||
M5.Lcd.setTextColor(COLORS[COLOR_MODE]);
|
|
||||||
|
|
||||||
// HEADER
|
|
||||||
if (COLOR_MODE == DARK) {
|
|
||||||
if (poorQuality < 50) {
|
|
||||||
M5.lcd.drawBitmap(5, 5, 50, 50, (uint16_t *) signal_white_3_3);
|
|
||||||
} else if ((50 < poorQuality) && (poorQuality < 150)) {
|
|
||||||
M5.lcd.drawBitmap(5, 5, 50, 50, (uint16_t *) signal_white_2_3);
|
|
||||||
} else if ((poorQuality == 200) || (poorQuality > 230)) {
|
|
||||||
M5.lcd.drawBitmap(5, 5, 50, 50, (uint16_t *) signal_white_0_3);
|
|
||||||
} else {
|
|
||||||
M5.lcd.drawBitmap(5, 5, 50, 50, (uint16_t *) signal_white_1_3);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (poorQuality < 50) {
|
|
||||||
M5.lcd.drawBitmap(5, 5, 50, 50, (uint16_t *) signal_3_3);
|
|
||||||
} else if ((50 < poorQuality) && (poorQuality < 150)) {
|
|
||||||
M5.lcd.drawBitmap(5, 5, 50, 50, (uint16_t *) signal_2_3);
|
|
||||||
} else if ((poorQuality == 200) || (poorQuality > 230)) {
|
|
||||||
M5.lcd.drawBitmap(5, 5, 50, 50, (uint16_t *) signal_0_3);
|
|
||||||
} else {
|
|
||||||
M5.lcd.drawBitmap(5, 5, 50, 50, (uint16_t *) signal_1_3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (poorQuality == 200) { // if headset not connected to person
|
|
||||||
for (int i = 0; i < 4; i++) { // thicker line
|
|
||||||
M5.lcd.drawLine(5 + i, 55, 55 + i, 5, RED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
M5.Lcd.drawString(String(poorQuality), 60, 20, 4);
|
|
||||||
|
|
||||||
if (COLOR_MODE == DARK) {
|
|
||||||
M5.lcd.drawBitmap(280, 5, 30, 50, (uint16_t *) bluetooth_dark);
|
|
||||||
} else {
|
|
||||||
M5.lcd.drawBitmap(280, 5, 30, 50, (uint16_t *) bluetooth_light);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isConnected == false) { // IF BLUETOOTH DISCONNECTED
|
|
||||||
for (int i = 0; i < 4; i++) { // thicker line
|
|
||||||
M5.lcd.drawLine(280 + i, 55, 310 + i, 5, RED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
M5.Lcd.drawLine(0, 60, 320, 60, COLORS[COLOR_MODE]);
|
|
||||||
|
|
||||||
// BODY
|
|
||||||
M5.Lcd.drawString("Attention", 20, 80, 4);
|
|
||||||
M5.Lcd.drawString("Meditation", 160, 80, 4);
|
|
||||||
|
|
||||||
M5.Lcd.drawString(String(attention), 60, 130, 4);
|
|
||||||
M5.Lcd.drawString(String(meditation), 220, 130, 4);
|
|
||||||
|
|
||||||
// BOTTOM
|
|
||||||
M5.Lcd.drawLine(0, 180, 320, 180, COLORS[COLOR_MODE]);
|
|
||||||
screenModeButton.draw();
|
|
||||||
if (COLOR_MODE == DARK) {
|
|
||||||
M5.lcd.drawBitmap(0, 191, 50, 50, (uint16_t *) modeSwitchDark);
|
|
||||||
M5.lcd.drawBitmap(55, 191, 50, 50, (uint16_t *) brightness_white);
|
|
||||||
M5.lcd.drawBitmap(200, 191, 45, 50, (uint16_t *) restart_white);
|
|
||||||
M5.lcd.drawBitmap(260, 186, 55, 55, (uint16_t *) power_off_white);
|
|
||||||
} else {
|
|
||||||
M5.lcd.drawBitmap(0, 191, 50, 50, (uint16_t *) modeSwitchLight);
|
|
||||||
M5.lcd.drawBitmap(55, 191, 50, 50, (uint16_t *) brightness);
|
|
||||||
M5.lcd.drawBitmap(200, 191, 45, 50, (uint16_t *) restart);
|
|
||||||
M5.lcd.drawBitmap(260, 186, 55, 55, (uint16_t *) power_off);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void screenModeHandler(Event& e) {
|
|
||||||
M5.Axp.SetLDOEnable(3, true);
|
|
||||||
delay(125);
|
|
||||||
M5.Axp.SetLDOEnable(3, false);
|
|
||||||
if (COLOR_MODE == DARK) {
|
|
||||||
COLOR_MODE = LIGHT;
|
|
||||||
} else {
|
|
||||||
COLOR_MODE = DARK;
|
|
||||||
}
|
|
||||||
|
|
||||||
drawGui(COLOR_MODE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void brightnessHandler(Event& e) {
|
|
||||||
M5.Axp.SetLDOEnable(3, true);
|
|
||||||
delay(125);
|
|
||||||
M5.Axp.SetLDOEnable(3, false);
|
|
||||||
switch (brightnessValue) {
|
|
||||||
case 1:
|
|
||||||
M5.Axp.SetLcdVoltage(2500);
|
|
||||||
brightnessValue += 1;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
M5.Axp.SetLcdVoltage(2750);
|
|
||||||
brightnessValue += 1;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
M5.Axp.SetLcdVoltage(2900);
|
|
||||||
brightnessValue += 1;
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
M5.Axp.SetLcdVoltage(3150);
|
|
||||||
brightnessValue = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void restartHandler(Event& e) {
|
|
||||||
M5.Axp.SetLDOEnable(3, true);
|
|
||||||
delay(250);
|
|
||||||
M5.Axp.SetLDOEnable(3, false);
|
|
||||||
M5.shutdown(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void powerOffHandler(Event& e) {
|
|
||||||
M5.Axp.SetLDOEnable(3, true);
|
|
||||||
delay(500);
|
|
||||||
M5.Axp.SetLDOEnable(3, false);
|
|
||||||
M5.shutdown();
|
|
||||||
}
|
|
|
@ -1,8 +1,13 @@
|
||||||
#define DEBUGOUTPUT 0
|
#include <map> // Bluetooth
|
||||||
#include <map> // Bluetooth
|
|
||||||
#include <BluetoothSerial.h> // Bluetooth
|
#include <BluetoothSerial.h> // Bluetooth
|
||||||
#include <M5Core2.h>
|
#include <M5Core2.h>
|
||||||
#include "icons.cpp" // icons
|
#include "icons.cpp" // icons
|
||||||
|
#include <Preferences.h> // Library for writing in the memory
|
||||||
|
|
||||||
|
// store screen mode and brightness in the memory of the Core
|
||||||
|
Preferences preferences;
|
||||||
|
const char* key1 = "screenMode";
|
||||||
|
const char* key2 = "brightness";
|
||||||
|
|
||||||
// UART communication variables
|
// UART communication variables
|
||||||
#define RX_PIN 19
|
#define RX_PIN 19
|
||||||
|
@ -11,7 +16,8 @@ String stringReceived = "";
|
||||||
int valueReceived = 0;
|
int valueReceived = 0;
|
||||||
|
|
||||||
// GUI variables
|
// GUI variables
|
||||||
int brightnessValue = 1;
|
int brightnessValues[4] = {2500, 2750, 2900, 3150};
|
||||||
|
int brightnessValue;
|
||||||
ButtonColors noDraw = { NODRAW, NODRAW, NODRAW };
|
ButtonColors noDraw = { NODRAW, NODRAW, NODRAW };
|
||||||
Button screenModeButton(0, 190, 50, 50, false, "", noDraw);
|
Button screenModeButton(0, 190, 50, 50, false, "", noDraw);
|
||||||
Button brightnessButton(65, 190, 50, 50, false, "", noDraw);
|
Button brightnessButton(65, 190, 50, 50, false, "", noDraw);
|
||||||
|
@ -19,9 +25,8 @@ Button restartButton(200, 191, 45, 50, false, "", noDraw);
|
||||||
Button powerOffButton(260, 186, 55, 55, false, "", noDraw);
|
Button powerOffButton(260, 186, 55, 55, false, "", noDraw);
|
||||||
boolean buttonPressed = false;
|
boolean buttonPressed = false;
|
||||||
|
|
||||||
enum screenMode { DARK,
|
enum screenMode {DARK, LIGHT};
|
||||||
LIGHT };
|
screenMode COLOR_MODE;
|
||||||
screenMode COLOR_MODE = LIGHT;
|
|
||||||
|
|
||||||
uint16_t COLORS[2] = { WHITE, BLACK };
|
uint16_t COLORS[2] = { WHITE, BLACK };
|
||||||
|
|
||||||
|
@ -47,6 +52,11 @@ uint32_t eegPower[8];
|
||||||
// system variables
|
// system variables
|
||||||
long lastReceivedPacket = 0;
|
long lastReceivedPacket = 0;
|
||||||
boolean bigPacket = false;
|
boolean bigPacket = false;
|
||||||
|
float batVoltage;
|
||||||
|
float batPercentage;
|
||||||
|
boolean lock = false;
|
||||||
|
|
||||||
|
// Multi threading
|
||||||
TaskHandle_t TaskHandle_1;
|
TaskHandle_t TaskHandle_1;
|
||||||
TaskHandle_t TaskHandle_2;
|
TaskHandle_t TaskHandle_2;
|
||||||
|
|
||||||
|
@ -56,7 +66,16 @@ TaskHandle_t TaskHandle_2;
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
M5.begin();
|
M5.begin();
|
||||||
M5.Axp.SetLcdVoltage(3150); // Set initial brightness
|
|
||||||
|
preferences.begin("Key");
|
||||||
|
Serial.print(preferences.getUInt(key1));
|
||||||
|
if (preferences.getUInt(key1) == 0) {
|
||||||
|
COLOR_MODE = DARK;
|
||||||
|
} else {
|
||||||
|
COLOR_MODE = LIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
M5.Axp.SetLcdVoltage(brightnessValues[preferences.getUInt(key2)]); // Set initial brightness
|
||||||
Serial2.begin(9600, SERIAL_8N1, RX_PIN, TX_PIN); // UART communication
|
Serial2.begin(9600, SERIAL_8N1, RX_PIN, TX_PIN); // UART communication
|
||||||
|
|
||||||
// MULTITHREADING
|
// MULTITHREADING
|
||||||
|
@ -114,7 +133,7 @@ void task1(void *pvParameters) {
|
||||||
Serial.printf("connecting to %s - %d\n", addr.toString().c_str(), channel);
|
Serial.printf("connecting to %s - %d\n", addr.toString().c_str(), channel);
|
||||||
SerialBT.connect(addr, channel, sec_mask, role);
|
SerialBT.connect(addr, channel, sec_mask, role);
|
||||||
isConnected = true;
|
isConnected = true;
|
||||||
//drawGui(COLOR_MODE);
|
drawGui(COLOR_MODE);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Serial.println("Didn't find any devices");
|
Serial.println("Didn't find any devices");
|
||||||
|
@ -123,6 +142,7 @@ void task1(void *pvParameters) {
|
||||||
} else {
|
} else {
|
||||||
Serial.println("Error on discoverAsync f.e. not workin after a \"connect\"");
|
Serial.println("Error on discoverAsync f.e. not workin after a \"connect\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
if (!SerialBT.connected() && isConnected == true) {
|
if (!SerialBT.connected() && isConnected == true) {
|
||||||
isConnected = false;
|
isConnected = false;
|
||||||
|
@ -143,113 +163,110 @@ void task1(void *pvParameters) {
|
||||||
|
|
||||||
if (!SerialBT.isClosed() && SerialBT.connected()) {
|
if (!SerialBT.isClosed() && SerialBT.connected()) {
|
||||||
if (ReadOneByte() == 170) {
|
if (ReadOneByte() == 170) {
|
||||||
if (ReadOneByte() == 170) {
|
payloadLength = ReadOneByte();
|
||||||
payloadLength = ReadOneByte();
|
if (payloadLength > 169) //Payload length can not be greater than 169
|
||||||
if (payloadLength > 169) //Payload length can not be greater than 169
|
return;
|
||||||
return;
|
|
||||||
|
|
||||||
generatedChecksum = 0;
|
generatedChecksum = 0;
|
||||||
|
|
||||||
for (int i = 0; i < payloadLength; i++) {
|
for (int i = 0; i < payloadLength; i++) {
|
||||||
payloadData[i] = ReadOneByte(); //Read payload into memory
|
payloadData[i] = ReadOneByte(); //Read payload into memory
|
||||||
generatedChecksum += payloadData[i];
|
generatedChecksum += payloadData[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
checksum = ReadOneByte(); //Read checksum byte from stream
|
||||||
|
generatedChecksum = 255 - generatedChecksum; //Take one's compliment of generated checksum
|
||||||
|
|
||||||
|
if (checksum == generatedChecksum) {
|
||||||
|
|
||||||
|
for (int i = 0; i < payloadLength; i++) { // Parse the payload
|
||||||
|
switch (payloadData[i]) {
|
||||||
|
case 2:
|
||||||
|
i++;
|
||||||
|
poorQuality = payloadData[i];
|
||||||
|
bigPacket = true;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
i++;
|
||||||
|
attention = payloadData[i];
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
i++;
|
||||||
|
meditation = payloadData[i];
|
||||||
|
break;
|
||||||
|
case 0x80:
|
||||||
|
i++;
|
||||||
|
raw = (payloadData[i++] << 8) | payloadData[i++];
|
||||||
|
break;
|
||||||
|
case 0x83:
|
||||||
|
for (int j = 0; j < 8; j++) {
|
||||||
|
uint8_t a, b, c;
|
||||||
|
a = payloadData[++i];
|
||||||
|
b = payloadData[++i];
|
||||||
|
c = payloadData[++i];
|
||||||
|
eegPower[j] = ((uint32_t)a << 16) | ((uint32_t)b << 8) | (uint32_t)c;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
checksum = ReadOneByte(); //Read checksum byte from stream
|
if (bigPacket) {
|
||||||
generatedChecksum = 255 - generatedChecksum; //Take one's compliment of generated checksum
|
updatePoorQuality();
|
||||||
|
updateAttention();
|
||||||
|
updateMeditation();
|
||||||
|
|
||||||
if (checksum == generatedChecksum) {
|
// UART OUTPUT
|
||||||
|
switch (valueReceived) {
|
||||||
for (int i = 0; i < payloadLength; i++) { // Parse the payload
|
case 0:
|
||||||
switch (payloadData[i]) {
|
break;
|
||||||
case 2:
|
case 1:
|
||||||
i++;
|
Serial2.print(attention);
|
||||||
poorQuality = payloadData[i];
|
break;
|
||||||
bigPacket = true;
|
case 2:
|
||||||
break;
|
Serial2.print(meditation);
|
||||||
case 4:
|
break;
|
||||||
i++;
|
case 3:
|
||||||
attention = payloadData[i];
|
Serial2.print(poorQuality);
|
||||||
break;
|
break;
|
||||||
case 5:
|
|
||||||
i++;
|
|
||||||
meditation = payloadData[i];
|
|
||||||
break;
|
|
||||||
case 0x80:
|
|
||||||
i++;
|
|
||||||
raw = (payloadData[i++] << 8) | payloadData[i++];
|
|
||||||
break;
|
|
||||||
case 0x83:
|
|
||||||
for (int j = 0; j < 8; j++) {
|
|
||||||
uint8_t a, b, c;
|
|
||||||
a = payloadData[++i];
|
|
||||||
b = payloadData[++i];
|
|
||||||
c = payloadData[++i];
|
|
||||||
eegPower[j] = ((uint32_t)a << 16) | ((uint32_t)b << 8) | (uint32_t)c;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
} // switch
|
|
||||||
} // for loop
|
|
||||||
|
|
||||||
#if !DEBUGOUTPUT
|
|
||||||
if (bigPacket) {
|
|
||||||
updatePoorQuality();
|
|
||||||
updateAttention();
|
|
||||||
updateMeditation();
|
|
||||||
|
|
||||||
// UART OUTPUT
|
|
||||||
switch (valueReceived) {
|
|
||||||
case 0:
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
Serial2.print(attention);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
Serial2.print(meditation);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
Serial2.print(poorQuality);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// SERIAL OUTPUT
|
|
||||||
Serial.print("Attention: ");
|
|
||||||
Serial.print(attention);
|
|
||||||
Serial.print("\n");
|
|
||||||
Serial.print("Poor quality: ");
|
|
||||||
Serial.print(poorQuality);
|
|
||||||
Serial.print("\n");
|
|
||||||
Serial.print("Meditation: ");
|
|
||||||
Serial.print(meditation);
|
|
||||||
Serial.print("\n");
|
|
||||||
Serial.print("Raw: ");
|
|
||||||
Serial.print(raw);
|
|
||||||
Serial.print("\n");
|
|
||||||
Serial.print("EEG POWER: ");
|
|
||||||
Serial.print(String(eegPower[0]) + ", " + String(eegPower[1]) + ", " + String(eegPower[2]) + ", " + String(eegPower[3]) + ", " + String(eegPower[4]) + ", " + String(eegPower[5]) + ", " + String(eegPower[6]) + ", " + String(eegPower[7]));
|
|
||||||
Serial.print("\n");
|
|
||||||
} else {
|
|
||||||
Serial.println("NOT!");
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
bigPacket = false;
|
// SERIAL OUTPUT
|
||||||
} else {
|
Serial.print("Attention: ");
|
||||||
// Checksum Error
|
Serial.print(attention);
|
||||||
} // end if else for checksum
|
Serial.print("\n");
|
||||||
} // end if read 0xAA byte
|
Serial.print("Poor quality: ");
|
||||||
|
Serial.print(poorQuality);
|
||||||
|
Serial.print("\n");
|
||||||
|
Serial.print("Meditation: ");
|
||||||
|
Serial.print(meditation);
|
||||||
|
Serial.print("\n");
|
||||||
|
Serial.print("Raw: ");
|
||||||
|
Serial.print(raw);
|
||||||
|
Serial.print("\n");
|
||||||
|
Serial.print("EEG POWER: ");
|
||||||
|
Serial.print(String(eegPower[0]) + ", " + String(eegPower[1]) + ", " + String(eegPower[2]) + ", " + String(eegPower[3]) + ", " + String(eegPower[4]) + ", " + String(eegPower[5]) + ", " + String(eegPower[6]) + ", " + String(eegPower[7]));
|
||||||
|
Serial.print("\n");
|
||||||
|
}
|
||||||
|
bigPacket = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Serial.println("NOT CONNECTED!");
|
Serial.println("Bluetooth not connected!");
|
||||||
delay(1000);
|
vTaskDelete(TaskHandle_1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void task2(void *pvParameters) {
|
void task2(void *pvParameters) {
|
||||||
bluetoothLoading();
|
bluetoothLoading();
|
||||||
vTaskDelete(TaskHandle_2);
|
while (1) {
|
||||||
|
if (!lock) {
|
||||||
|
drawBattery();
|
||||||
|
}
|
||||||
|
delay(500);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawGui(screenMode COLOR_MODE) {
|
void drawGui(screenMode COLOR_MODE) {
|
||||||
|
@ -261,17 +278,19 @@ void drawGui(screenMode COLOR_MODE) {
|
||||||
updatePoorQuality();
|
updatePoorQuality();
|
||||||
|
|
||||||
if (COLOR_MODE == DARK) {
|
if (COLOR_MODE == DARK) {
|
||||||
M5.lcd.drawBitmap(280, 5, 30, 50, (uint16_t *)bluetoothDark);
|
M5.lcd.drawBitmap(240, 5, 30, 50, (uint16_t *)bluetoothDark);
|
||||||
} else {
|
} else {
|
||||||
M5.lcd.drawBitmap(280, 5, 30, 50, (uint16_t *)bluetoothLight);
|
M5.lcd.drawBitmap(240, 5, 30, 50, (uint16_t *)bluetoothLight);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isConnected == false && !connecting) { // IF BLUETOOTH DISCONNECTED
|
if (isConnected == false && !connecting) { // IF BLUETOOTH DISCONNECTED
|
||||||
for (int i = 0; i < 4; i++) { // thicker line
|
for (int i = 0; i < 4; i++) { // thicker line
|
||||||
M5.lcd.drawLine(280 + i, 55, 310 + i, 5, RED);
|
M5.lcd.drawLine(240 + i, 55, 270 + i, 5, RED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
drawBattery();
|
||||||
|
|
||||||
M5.Lcd.drawLine(0, 60, 320, 60, COLORS[COLOR_MODE]);
|
M5.Lcd.drawLine(0, 60, 320, 60, COLORS[COLOR_MODE]);
|
||||||
|
|
||||||
// BODY
|
// BODY
|
||||||
|
@ -306,10 +325,6 @@ byte ReadOneByte() {
|
||||||
if (!SerialBT.isClosed() && SerialBT.connected()) {
|
if (!SerialBT.isClosed() && SerialBT.connected()) {
|
||||||
ByteRead = SerialBT.read();
|
ByteRead = SerialBT.read();
|
||||||
|
|
||||||
#if DEBUGOUTPUT
|
|
||||||
Serial.print((char)ByteRead); // echo the same byte out the USB serial (for debug purposes)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return ByteRead;
|
return ByteRead;
|
||||||
} else {
|
} else {
|
||||||
Serial.println("not connected");
|
Serial.println("not connected");
|
||||||
|
@ -323,41 +338,47 @@ void screenModeHandler(Event &e) {
|
||||||
M5.Axp.SetLDOEnable(3, false);
|
M5.Axp.SetLDOEnable(3, false);
|
||||||
if (COLOR_MODE == DARK) {
|
if (COLOR_MODE == DARK) {
|
||||||
COLOR_MODE = LIGHT;
|
COLOR_MODE = LIGHT;
|
||||||
|
preferences.putUInt(key1, 1);
|
||||||
} else {
|
} else {
|
||||||
COLOR_MODE = DARK;
|
COLOR_MODE = DARK;
|
||||||
|
preferences.putUInt(key1, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
buttonPressed = true;
|
buttonPressed = true;
|
||||||
if (!connecting) {
|
if (!connecting) {
|
||||||
|
lock = true;
|
||||||
drawGui(COLOR_MODE);
|
drawGui(COLOR_MODE);
|
||||||
|
lock = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void bluetoothLoading() {
|
void bluetoothLoading() {
|
||||||
while (connecting) {
|
while (connecting) {
|
||||||
if (buttonPressed) {
|
if (buttonPressed) {
|
||||||
drawGui(COLOR_MODE);
|
drawGui(COLOR_MODE);
|
||||||
buttonPressed = false;
|
buttonPressed = false;
|
||||||
}
|
}
|
||||||
if (COLOR_MODE == DARK) {
|
if (COLOR_MODE == DARK) {
|
||||||
M5.lcd.fillRect(280, 5, 35, 51, BLACK);
|
M5.lcd.fillRect(240, 5, 35, 51, BLACK);
|
||||||
delay(500);
|
delay(500);
|
||||||
|
drawBattery();
|
||||||
if (!buttonPressed) {
|
if (!buttonPressed) {
|
||||||
M5.lcd.drawBitmap(280, 5, 30, 50, (uint16_t *)bluetoothDark);
|
M5.lcd.drawBitmap(240, 5, 30, 50, (uint16_t *)bluetoothDark);
|
||||||
delay(500);
|
delay(500);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
M5.lcd.fillRect(280, 5, 35, 51, WHITE);
|
M5.lcd.fillRect(240, 5, 35, 51, WHITE);
|
||||||
delay(500);
|
delay(500);
|
||||||
|
drawBattery();
|
||||||
if (!buttonPressed) {
|
if (!buttonPressed) {
|
||||||
M5.lcd.drawBitmap(280, 5, 30, 50, (uint16_t *)bluetoothLight);
|
M5.lcd.drawBitmap(240, 5, 30, 50, (uint16_t *)bluetoothLight);
|
||||||
delay(500);
|
delay(500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!connecting) {
|
if (!connecting) {
|
||||||
for (int i = 0; i < 4; i++) { // thicker line
|
for (int i = 0; i < 4; i++) { // thicker line
|
||||||
M5.lcd.drawLine(280 + i, 55, 310 + i, 5, RED);
|
M5.lcd.drawLine(240 + i, 55, 270 + i, 5, RED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -420,6 +441,45 @@ void updateMeditation() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void drawBattery() {
|
||||||
|
batVoltage = M5.Axp.GetBatVoltage();
|
||||||
|
batPercentage = (batVoltage < 3.2) ? 0 : (batVoltage - 3.2) * 100;
|
||||||
|
|
||||||
|
if (COLOR_MODE == DARK) {
|
||||||
|
if (M5.Axp.isCharging()) {
|
||||||
|
M5.lcd.drawBitmap(280, 5, 32, 50, (uint16_t *) batteryCharging_white);
|
||||||
|
} else {
|
||||||
|
if (batPercentage > 90) {
|
||||||
|
M5.lcd.drawBitmap(280, 5, 32, 50, (uint16_t *) batteryStatus4_white);
|
||||||
|
} else if ((90 > batPercentage) && (batPercentage > 60)) {
|
||||||
|
M5.lcd.drawBitmap(280, 5, 32, 50, (uint16_t *) batteryStatus3_white);
|
||||||
|
} else if ((60 > batPercentage) && (batPercentage > 20)) {
|
||||||
|
M5.lcd.drawBitmap(280, 5, 32, 50, (uint16_t *) batteryStatus2_white);
|
||||||
|
} else if ((20 > batPercentage) && (batPercentage > 10)) {
|
||||||
|
M5.lcd.drawBitmap(280, 5, 32, 50, (uint16_t *) batteryStatus1_white);
|
||||||
|
} else {
|
||||||
|
M5.lcd.drawBitmap(280, 5, 32, 50, (uint16_t *) batteryStatus0_white);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (M5.Axp.isCharging()) {
|
||||||
|
M5.lcd.drawBitmap(280, 5, 32, 50, (uint16_t *) batteryCharging);
|
||||||
|
} else {
|
||||||
|
if (batPercentage > 90) {
|
||||||
|
M5.lcd.drawBitmap(280, 5, 32, 50, (uint16_t *) batteryStatus4);
|
||||||
|
} else if ((90 > batPercentage) && (batPercentage > 60)) {
|
||||||
|
M5.lcd.drawBitmap(280, 5, 32, 50, (uint16_t *) batteryStatus3);
|
||||||
|
} else if ((60 > batPercentage) && (batPercentage > 20)) {
|
||||||
|
M5.lcd.drawBitmap(280, 5, 32, 50, (uint16_t *) batteryStatus2);
|
||||||
|
} else if ((20 > batPercentage) && (batPercentage > 10)) {
|
||||||
|
M5.lcd.drawBitmap(280, 5, 32, 50, (uint16_t *) batteryStatus1);
|
||||||
|
} else {
|
||||||
|
M5.lcd.drawBitmap(280, 5, 32, 50, (uint16_t *) batteryStatus0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int countDigit(int n) {
|
int countDigit(int n) {
|
||||||
if (n == 0)
|
if (n == 0)
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -435,24 +495,21 @@ void brightnessHandler(Event &e) {
|
||||||
M5.Axp.SetLDOEnable(3, true);
|
M5.Axp.SetLDOEnable(3, true);
|
||||||
delay(125);
|
delay(125);
|
||||||
M5.Axp.SetLDOEnable(3, false);
|
M5.Axp.SetLDOEnable(3, false);
|
||||||
switch (brightnessValue) {
|
switch (preferences.getUInt(key2)) {
|
||||||
|
case 0:
|
||||||
|
preferences.putUInt(key2, 1);
|
||||||
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
M5.Axp.SetLcdVoltage(2500);
|
preferences.putUInt(key2, 2);
|
||||||
brightnessValue += 1;
|
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
M5.Axp.SetLcdVoltage(2750);
|
preferences.putUInt(key2, 3);
|
||||||
brightnessValue += 1;
|
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
M5.Axp.SetLcdVoltage(2900);
|
preferences.putUInt(key2, 0);
|
||||||
brightnessValue += 1;
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
M5.Axp.SetLcdVoltage(3150);
|
|
||||||
brightnessValue = 1;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
M5.Axp.SetLcdVoltage(brightnessValues[preferences.getUInt(key2)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void restartHandler(Event &e) {
|
void restartHandler(Event &e) {
|
|
@ -43,7 +43,7 @@ void drawGui(screenMode COLOR_MODE) {
|
||||||
M5.Lcd.drawString("100", 60, 20, 4);
|
M5.Lcd.drawString("100", 60, 20, 4);
|
||||||
|
|
||||||
if (COLOR_MODE == DARK) {
|
if (COLOR_MODE == DARK) {
|
||||||
M5.lcd.drawBitmap(280, 5, 30, 50, (uint16_t *) bluetooth_dark);
|
M5.lcd.drawBitmap(240, 5, 30, 50, (uint16_t *) bluetooth_dark);
|
||||||
} else {
|
} else {
|
||||||
M5.lcd.drawBitmap(240, 5, 30, 50, (uint16_t *) bluetooth_light);
|
M5.lcd.drawBitmap(240, 5, 30, 50, (uint16_t *) bluetooth_light);
|
||||||
}
|
}
|
Before Width: | Height: | Size: 915 B After Width: | Height: | Size: 915 B |
Before Width: | Height: | Size: 920 B After Width: | Height: | Size: 920 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 471 B After Width: | Height: | Size: 471 B |
Before Width: | Height: | Size: 471 B After Width: | Height: | Size: 471 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 612 B After Width: | Height: | Size: 612 B |
Before Width: | Height: | Size: 612 B After Width: | Height: | Size: 612 B |
Before Width: | Height: | Size: 815 B After Width: | Height: | Size: 815 B |
Before Width: | Height: | Size: 2 KiB After Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 372 B After Width: | Height: | Size: 372 B |
Before Width: | Height: | Size: 372 B After Width: | Height: | Size: 372 B |
Before Width: | Height: | Size: 345 B After Width: | Height: | Size: 345 B |
Before Width: | Height: | Size: 378 B After Width: | Height: | Size: 378 B |
Before Width: | Height: | Size: 234 B After Width: | Height: | Size: 234 B |
Before Width: | Height: | Size: 234 B After Width: | Height: | Size: 234 B |
Before Width: | Height: | Size: 225 B After Width: | Height: | Size: 225 B |
Before Width: | Height: | Size: 225 B After Width: | Height: | Size: 225 B |