// ################################################################################
// Programm: M5_StiP_BLE_DMM_V05B-ino
// Aufgabe:  M5_StickC Plus als Client for ANENG  V05B DMM
// Autor:    R. Hoermann hoermann@vom-kuhberg.de
// Version:  2022-01-07
// Hardware: M5-Stick C PLUS
// Extras:   ANENG V05B DMM mit Bluetooth Interface
//

//           +---------------------------+   Y -- Bluetooth -- Y   +---------------+
//           |ESP32 StickCPlus    LED(#) |   |                 |   |ANENG V05B  DMM|
//           |      +------------------+ o---+                 +---o +-----------+ |
//           |      | LCD 240x135      | |                         | |LCD Display| |
//           |BtnA  | 64k Farben       | |                         | |Monochrom  | |
//           |      |                  | |                         | +-----------+ |
//           |      |                  | |                         | [SEL]   [H/Z] |
//           |      +------------------+ |                         |      (o)      |
//           |                 BtnB      |                         |     [ >B]     |
//           +---------------------------+                         |     [ U ]     |
//                                                                 | (O)       (O) |
//                                                                 |      (O)      |
//                                                                 +---------------+
// #################################################################################

#include 
#include 

// The remote service UUID we wish to connect to.
#define  UUID_S "0000fff0-0000-1000-8000-00805f9b34fb"
// The characteristic UUID of the remote service we are interested in=DATA= Handle 9
#define  UUID_C "0000fff4-0000-1000-8000-00805f9b34fb"

static   BLEUUID UUID_SERV(UUID_S);
static   BLEUUID UUID_CHAR(UUID_C);

static   boolean b_doConnect = false;
static   boolean b_connected = false;
static   boolean b_doScan    = false;
static   BLERemoteCharacteristic* pRemoteCharacteristic;
static   BLEAdvertisedDevice* myDevice;
int      ai_XOR[] = {0x41,0x21,0x73,0x55,0xa2,0xc1,0x32,0x71,0x66,0xaa,0x3b,0xd0,0xe2};
int      ai_DMM[10];
long     l_ms0;         // Store the beginning of Scan for BLE Devices

String   s_SevSeg="";
String   s_DMM="";      // Value
String   s_PRE="";      // Prefix M k _ m mikro n
String   s_UNIT="";     // 'C 'F V A Ohm Hz
String   s_OPT="";      // Optionen BT,HOLD,DC,AC

#define  pin_M5_LED 10  // OnBoard RED LED on M5 StickCPlus

int  msb(int lsb)
{
	int mm=128,ml=1,b=0;
	for(int i=0;i<8;i++){if((lsb & mm)==mm){b+=ml;} mm=mm/2; ml=ml*2;}
	return b;
}

void digit(int dig)
{
	s_SevSeg+=String(dig,HEX)+" ";
	if(dig>127){s_DMM+=".";}
	int d7=dig & 0x7f;
	if(d7==0)   {s_DMM+=" "; return;}
	if(d7==0x7d){s_DMM+="0"; return;}
	if(d7==0x05){s_DMM+="1"; return;}
	if(d7==0x5b){s_DMM+="2"; return;}
	if(d7==0x1f){s_DMM+="3"; return;}
	if(d7==0x27){s_DMM+="4"; return;}
	if(d7==0x3e){s_DMM+="5"; return;}
	if(d7==0x7e){s_DMM+="6"; return;}
	if(d7==0x15){s_DMM+="7"; return;}
	if(d7==0x7f){s_DMM+="8"; return;}
	if(d7==0x3f){s_DMM+="9"; return;}
	if(d7==0x77){s_DMM+="A"; return;}
	if(d7==0x4c){s_DMM+="u"; return;}
	if(d7==0x6a){s_DMM+="t"; return;}
	if(d7==0x4e){s_DMM+="o"; return;}
	if(d7==0x68){s_DMM+="L"; return;}
	s_DMM+="_"+String(d7,HEX)+"_";
}

void DMM_begin()
{
	M5.Lcd.fillScreen(BLACK); M5.Lcd.fillRect(0,0,240,24,YELLOW);
	M5.Lcd.drawLine( 2, 4,10,16,BLUE); M5.Lcd.drawLine(10,16, 6,20,BLUE);
	M5.Lcd.drawLine( 6,20, 6, 0,BLUE);
	M5.Lcd.drawLine( 6, 0,10, 8,BLUE); M5.Lcd.drawLine(10, 8, 2,16,BLUE);
	M5.Lcd.setTextColor(BLACK); M5.Lcd.drawString("ANENG V05B DMM",16,2,4);
	M5.Lcd.setTextColor(WHITE);
}

void decode_DMM()
{
	M5.Lcd.setTextColor(BLACK);
	M5.Lcd.drawCentreString(s_DMM,90,40,7);
	M5.Lcd.drawString(s_UNIT,162,64,4);
	M5.Lcd.drawString(s_PRE, 162,40,4);
	M5.Lcd.drawString(s_OPT,   8,98,2);
	M5.Lcd.setTextColor(WHITE);
	s_DMM=""; s_SevSeg=""; s_OPT="BT "; s_UNIT=""; s_PRE="";
	digit(16*(ai_DMM[3]%16)+ai_DMM[4]/16);
	digit(16*(ai_DMM[4]%16)+ai_DMM[5]/16);
	digit(16*(ai_DMM[5]%16)+ai_DMM[6]/16);
	digit(16*(ai_DMM[6]%16)+ai_DMM[7]/16);
	Serial.print(s_SevSeg+"=>"+s_DMM);
//	M5.Lcd.fillRect(0,40,240,80,BLACK);

	if(ai_DMM[3] & 0x40){s_OPT+= "HOLD "; }

	if(ai_DMM[8] & 0x80){s_PRE+= "nano";  }
	if(ai_DMM[8] & 0x40){s_UNIT+="Volt";  }
	if(ai_DMM[8] & 0x20){s_OPT+= "( )";   } //?
	if(ai_DMM[8] & 0x10){s_OPT+= "BT ";   } //?
	if(ai_DMM[8] & 0x08){s_UNIT+="Farad"; }
	if(ai_DMM[8] & 0x04){s_OPT+= " -|>- ";}
	if(ai_DMM[8] & 0x02){s_OPT+= " :) ";  } //? Speaker
	if(ai_DMM[8] & 0x01){s_PRE+= "micro"; }

	if(ai_DMM[9] & 0x80){s_UNIT+="Ohm";   }
	if(ai_DMM[9] & 0x40){s_PRE+= "kilo";  }
	if(ai_DMM[9] & 0x20){s_PRE+= "milli"; } // ?
	if(ai_DMM[9] & 0x10){s_PRE+= "Mega";  }
	if(ai_DMM[9] & 0x08){s_UNIT+="Ampere";} // ?
	if(ai_DMM[9] & 0x04){s_UNIT+="Hz";    }
	if(ai_DMM[9] & 0x02){s_UNIT+="'F";    }
	if(ai_DMM[9] & 0x01){s_UNIT+="'C";    }

	if(s_DMM=="Auto"){s_PRE+="Auto";}
	if(s_UNIT=="Volt" ){M5.Lcd.setTextColor(BLUE);}
	if(s_UNIT=="Farad"){M5.Lcd.setTextColor(MAGENTA);}
	if(s_UNIT=="Ohm"  ){M5.Lcd.setTextColor(YELLOW);}
	if(s_UNIT=="'C"   ){M5.Lcd.setTextColor(RED);}
	if(s_UNIT=="'F"   ){M5.Lcd.setTextColor(CYAN);}
	M5.Lcd.drawCentreString(s_DMM,90,40,7);
	M5.Lcd.drawString(s_PRE, 162,40,4);
	M5.Lcd.drawString(s_UNIT,162,64,4);
	M5.Lcd.drawString(s_OPT,   8,98,2);
}

static void notifyCallback
(BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify)
{
	Serial.printf("%8.3f Notify for UUID_C Data[%2d] RAW[]=[",millis()/1000.0,length);
	for(int i=0;igetAddress().toString().c_str());
	BLEClient*  pClient  = BLEDevice::createClient();
	Serial.println(" - Created client");
	pClient->setClientCallbacks(new MyClientCallback());
	pClient->connect(myDevice);  // Connect to the remove BLE Server.
	Serial.println(" - Connected to server");

	BLERemoteService* pRemoteService = pClient->getService(UUID_SERV);
	if (pRemoteService == nullptr)
	{
		Serial.print("Failed to find our service UUID: ");
		Serial.println(UUID_SERV.toString().c_str());
		pClient->disconnect();
		return false;
	}
	Serial.printf(" - Found our service %s\r\n",UUID_S);

	Serial.printf(" - Check for UUID_C   %s\r\n",UUID_C);
	pRemoteCharacteristic = pRemoteService->getCharacteristic(UUID_CHAR);
	if (pRemoteCharacteristic == nullptr)
	{
		Serial.println("Failed to find our characteristic UUID: ");
		pClient->disconnect(); return false;
	}
	Serial.println(" - Found our UUID_C");

	if(pRemoteCharacteristic->canRead()) // Read the value of the characteristic.
	{
		std::string value = pRemoteCharacteristic->readValue();
		Serial.printf("The characteristic value was:%s\r\n",value.c_str());
	}

	if(pRemoteCharacteristic->canNotify())
	{
		Serial.println("Device can NOTIFY!! Register Notify-Callback for it"); delay(1000);
		pRemoteCharacteristic->registerForNotify(notifyCallback);
	}
	b_connected = true;
}

// Scan for BLE servers and find the first one that advertises the service we are looking for.
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks
{
	void onResult(BLEAdvertisedDevice advertisedDevice)
	{
		Serial.print("BLE Advertised Device found: ");
		Serial.println(advertisedDevice.toString().c_str());
//		We have found a device, let us now see if it contains the service we are looking for.
		if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(UUID_SERV))
		{
			BLEDevice::getScan()->stop();
			myDevice    = new BLEAdvertisedDevice(advertisedDevice);
			b_doConnect = true;
			b_doScan    = true;
			Serial.printf("UUID_S found, therefore Scan stopped! after %d ms",millis()-l_ms0);
			digitalWrite(pin_M5_LED,HIGH); // LED off !!
			DMM_begin();
		}
	}
};

void setup()
{
	M5.begin(); pinMode(pin_M5_LED,OUTPUT); digitalWrite(pin_M5_LED,LOW); // LED is lowactiv
	Serial.println("Starting ESP32 as Bluetooth Low Energie Client ..");
	Serial.printf("Scan for Service UUID=%s\r\n",UUID_S);
	M5.Lcd.setRotation(3); M5.Lcd.fillScreen(BLACK);
	M5.Lcd.drawString("BLE-Scanner",0,0,4);
	M5.Lcd.setCursor(0,24);
	M5.Lcd.println("Scan for Service UUID");
	M5.Lcd.println(UUID_S);
	l_ms0=millis();
	BLEDevice::init("");
	BLEScan* pBLEScan = BLEDevice::getScan();
	pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
	pBLEScan->setInterval(1349);
	pBLEScan->setWindow(449);
	pBLEScan->setActiveScan(true);  // Specify that we want active scanning
	pBLEScan->start(5, false);      // AutoStop SCAN  after 5 seconds.
}

void loop()
{
	if(b_doConnect == true)
	{
		if(connectToServer())
			{ Serial.println("Now connected to the BLE Server."); }
		else{ Serial.println("FAILED to connect to the server."); }
		b_doConnect = false;
	}
	if(!b_connected & b_doScan)
	{	l_ms0=millis();	BLEDevice::getScan()->start(0);}  // SCAN if NOT Connected
	M5.update();
	if(M5.BtnA.wasPressed()){DMM_begin();}
}