Dali.cpp 8.71 KB

#include "Dali.h"
#include <SoftwareSerial.h>



Dali::Dali() //constructor
{
  applyWorkAround1Mhz = 0;
}


void Dali::setTxPin(uint8_t pin)
{
  TxPin = pin; // user sets the digital pin as output
  pinMode(TxPin, OUTPUT); 
  digitalWrite(TxPin, HIGH);
}

void Dali::setRxAnalogPin(uint8_t pin)
{
	RxAnalogPin = pin; // user sets the digital pin as output
}

void Dali::workAround1MhzTinyCore(uint8_t a)
{
  applyWorkAround1Mhz = a;
}

void Dali::setupAnalogReceive(uint8_t pin) 
{
	setRxAnalogPin(pin); // user sets the analog pin as input
}


void Dali::setupTransmit(uint8_t pin)
{
  setTxPin(pin);
  speedFactor = 2;
  //we don't use exact calculation of passed time spent outside of transmitter
  //because of high ovehead associated with it, instead we use this 
  //emprirically determined values to compensate for the time loss
  
  #if F_CPU == 1000000UL
    uint16_t compensationFactor = 88; //must be divisible by 8 for workaround
  #elif F_CPU == 8000000UL
    uint16_t compensationFactor = 12; 
  #else //16000000Mhz
    uint16_t compensationFactor = 4; 
  #endif  

#if (F_CPU == 80000000UL) || (F_CPU == 160000000)   // ESP8266 80MHz or 160 MHz
  delay1 = delay2 = (HALF_BIT_INTERVAL >> speedFactor) - 2;
#else
  delay1 = (HALF_BIT_INTERVAL >> speedFactor) - compensationFactor;
  delay2 = (HALF_BIT_INTERVAL >> speedFactor) - 2;
  
  #if F_CPU == 1000000UL
    delay2 -= 22; //22+2 = 24 is divisible by 8
    if (applyWorkAround1Mhz) { //definition of micro delay is broken for 1MHz speed in tiny cores as of now (May 2013)
      //this is a workaround that will allow us to transmit on 1Mhz
      //divide the wait time by 8
      delay1 >>= 3;
      delay2 >>= 3;
    }
  #endif
#endif

	}


void Dali::transmit(uint8_t cmd1, uint8_t cmd2) // transmit commands to DALI bus (address byte, command byte)
{
	sendBit(1);
	sendByte(cmd1);
	sendByte(cmd2);
	digitalWrite(TxPin, HIGH);
}


void Dali::sendByte(uint8_t b) 
{
	for (int i = 7; i >= 0; i--) 
	{
		sendBit((b >> i) & 1);
	}
}


void Dali::sendBit(int b) 
{		
 if (b) {
		sendOne();
	}
	else {
		sendZero();
	} 
}


void Dali::sendZero(void)
{
  digitalWrite(TxPin, HIGH);
  delayMicroseconds(delay2);
  digitalWrite(TxPin, LOW);
  delayMicroseconds(delay1);

}


void Dali::sendOne(void)
{
  digitalWrite(TxPin, LOW);
  delayMicroseconds(delay2);
  digitalWrite(TxPin, HIGH);
  delayMicroseconds(delay1);
}


void Dali::busTest() //DALI bus test
{
	int maxLevel;
	int minLevel;
	
	//Luminaries must turn on and turn off. If not, check connection.
	delay(100);
	dali.transmit(BROADCAST_C, OFF_C); //Broadcast ON
	delay(500);
	dali.transmit(BROADCAST_C, ON_C); //Broadcast OFF
	delay(100);
	while (!Serial);
	
	//Receive response from luminaries: max and min level
	dali.transmit(BROADCAST_C, QUERY_STATUS);
	maxLevel = dali.maxResponseLevel();
	dali.transmit(BROADCAST_C, QUERY_STATUS);
	minLevel = dali.minResponseLevel();

	dali.analogLevel = (int)(maxLevel + minLevel) / 2;
	
	
	


}


void Dali::splitAdd(long input, uint8_t &highbyte, uint8_t &middlebyte, uint8_t &lowbyte) 
{
	highbyte = input >> 16;
	middlebyte = input >> 8;
	lowbyte = input;
}



// define min response level
int Dali::minResponseLevel() 
{

	const uint8_t dalistep = 40; //us
	uint16_t rxmin = 1024;
	uint16_t dalidata;
	long idalistep;

	
	
	for (idalistep = 0; idalistep < dali.daliTimeout; idalistep = idalistep + dalistep) {
		dalidata = analogRead(RxAnalogPin);
		if (dalidata < rxmin) {
			rxmin = dalidata;
		};
		delayMicroseconds(dalistep);
	}
	return rxmin; 
}

// define max response level
int Dali::maxResponseLevel() 
{

	const uint8_t dalistep = 40; //us
	uint16_t rxmax = 0;
	uint16_t dalidata;
	long idalistep;

	
	for (idalistep = 0; idalistep < dali.daliTimeout; idalistep = idalistep + dalistep) {
		dalidata = analogRead(RxAnalogPin);
		if (dalidata > rxmax) {
			rxmax = dalidata;
		};
		delayMicroseconds(dalistep);
	}
	return rxmax;
}


//scan for individual short address
void Dali::scanShortAdd()
{

	const int delayTime = 10;
	const uint8_t start_ind_adress = 0;
	const uint8_t finish_ind_adress = 127;
	uint8_t add_byte;
	uint8_t device_short_add;
	String response;
		
	dali.transmit(BROADCAST_C, OFF_C); // Broadcast Off
	delay(delayTime);
	Serial.println("Short addresses:");

	for (device_short_add = start_ind_adress; device_short_add <= 63; device_short_add++) {

		add_byte = 1 + (device_short_add << 1); // convert short address to address byte

		
		dali.transmit(add_byte, QUERY_STATUS); //query status

			
		if (minResponseLevel() < dali.analogLevel) {
			response = "YES";
			dali.transmit(add_byte, ON_C); // switch on
			delay(1000);
			dali.transmit(add_byte, OFF_C); // switch off
			delay(1000);
		}
		else {
			response = "NO";
		}

		
		Serial.print("BIN: ");
		Serial.print(device_short_add, BIN);
		Serial.print(" ");
		Serial.print("DEC: ");
		Serial.print(device_short_add, DEC);
		Serial.print(" ");
		Serial.print("HEX: ");
		Serial.print(device_short_add, HEX);
		Serial.print(" ");
		Serial.print("Response: ");
		Serial.print(response);
		Serial.println();

	}

	dali.transmit(BROADCAST_C, ON_C); // Broadcast On
	Serial.println();
	delay(delayTime);

}


int Dali::readBinaryString(char *s) 
{
	int result = 0;
	while (*s) {
		result <<= 1;
		if (*s++ == '1') result |= 1;
	}
	return result;
}


bool Dali::cmdCheck(String & input, int & cmd1, int & cmd2) 
{
	bool test = true;

	input.replace(" ", "");   // Delete spaces

	if (input.length() != 16) {
		test = false; //check if command contain 16bit
	}
	else {
		for (int i = 0; i <= input.length() - 1; i++) {
			if ((int)input.charAt(i) == 49 or (int)input.charAt(i) == 48) {}
			else {
				test = false;
			};
		};
	};

	if (test) {
		cmd1 = readBinaryString(input.substring(0, 8).c_str());
		cmd2 = readBinaryString(input.substring(8, 16).c_str());
	}

	return test;
}

void Dali::initialisation() {

	const int delaytime = 10; //ms

	long low_longadd = 0x000000;
	long high_longadd = 0xFFFFFF;
	long longadd = (long)(low_longadd + high_longadd) / 2;
	uint8_t highbyte;
	uint8_t middlebyte;
	uint8_t lowbyte;
	uint8_t short_add = 0;
	uint8_t cmd2;

	
	delay(delaytime);
	dali.transmit(BROADCAST_C, RESET);
	delay(delaytime);
	dali.transmit(BROADCAST_C, RESET);
	delay(delaytime);
	dali.transmit(BROADCAST_C, OFF_C);
	delay(delaytime);
	dali.transmit(0b10100101, 0b00000000); //initialise
	delay(delaytime);
	dali.transmit(0b10100101, 0b00000000); //initialise
	delay(delaytime);
	dali.transmit(0b10100111, 0b00000000); //randomise
	delay(delaytime);
	dali.transmit(0b10100111, 0b00000000); //randomise

	Serial.println("Searching fo long addresses:");

	while (longadd <= 0xFFFFFF - 2 and short_add <= 64) {
		while ((high_longadd - low_longadd) > 1) {

			dali.splitAdd(longadd, highbyte, middlebyte, lowbyte); //divide 24bit adress into three 8bit adresses
			delay(delaytime);
			dali.transmit(0b10110001, highbyte); //search HB
			delay(delaytime);
			dali.transmit(0b10110011, middlebyte); //search MB
			delay(delaytime);
			dali.transmit(0b10110101, lowbyte); //search LB
			delay(delaytime);
			dali.transmit(0b10101001, 0b00000000); //compare
			
			if (minResponseLevel() > dali.analogLevel) 
			{
				low_longadd = longadd;
			}
			else 
			{
				high_longadd = longadd;
			}
			
			longadd = (low_longadd + high_longadd) / 2; //center

			Serial.print("BIN: ");
			Serial.print(longadd + 1, BIN);
			Serial.print(" ");
			Serial.print("DEC: ");
			Serial.print(longadd + 1, DEC);
			Serial.print(" ");
			Serial.print("HEX: ");
			Serial.print(longadd + 1, HEX);
			Serial.println();
		} // second while


		if (high_longadd != 0xFFFFFF) 
		{
			splitAdd(longadd + 1, highbyte, middlebyte, lowbyte);
			dali.transmit(0b10110001, highbyte); //search HB
			delay(delaytime);
			dali.transmit(0b10110011, middlebyte); //search MB
			delay(delaytime);
			dali.transmit(0b10110101, lowbyte); //search LB
			delay(delaytime);
			dali.transmit(0b10110111, 1 + (short_add << 1)); //program short adress
			delay(delaytime);
			dali.transmit(0b10101011, 0b00000000); //withdraw
			delay(delaytime);
			dali.transmit(1 + (short_add << 1), ON_C);
			delay(1000);
			dali.transmit(1 + (short_add << 1), OFF_C);
			delay(delaytime);
			short_add++;

			Serial.println("Assigning a short address");

			high_longadd = 0xFFFFFF;
			longadd = (low_longadd + high_longadd) / 2;

		}
		else {
			Serial.println("End");
		}
	} // first while


	dali.transmit(0b10100001, 0b00000000);  //terminate
	dali.transmit(BROADCAST_C, ON_C);  //broadcast on
}






	Dali dali;