// Cross Fading

// This example uses channels 1,2 and 3 to drive an RGB lamp and make it fade from one color to another.

#include "pins_arduino.h"

int sig = 3; // signal

int value = 0;
int valueadd = 3;

byte curr_rgb[3] = {0,0,0};

byte dmxChannel[64];

void setDmxChannel(byte channelID, byte value) {
        if (channelID < 64)
                dmxChannel[channelID] = value;
}

/* Sends a DMX byte out on a pin.  Assumes a 16 MHz clock.
 * Disables interrupts, which will disrupt the millis() function if used
 * too frequently. */
void shiftDmxOut(int pin, int theByte)
{
  int port_to_output[] = {
    NOT_A_PORT,
    NOT_A_PORT,
    _SFR_IO_ADDR(PORTB),
    _SFR_IO_ADDR(PORTC),
    _SFR_IO_ADDR(PORTD)
    };

    int portNumber = port_to_output[digitalPinToPort(pin)];
  int pinMask = digitalPinToBitMask(pin);

  // the first thing we do is to write te pin to high
  // it will be the mark between bytes. It may be also
  // high from before
  _SFR_BYTE(_SFR_IO8(portNumber)) |= pinMask;
  delayMicroseconds(10);

  // disable interrupts, otherwise the timer 0 overflow interrupt that
  // tracks milliseconds will make us delay longer than we want.
  cli();

  // DMX starts with a start-bit that must always be zero
  _SFR_BYTE(_SFR_IO8(portNumber)) &= ~pinMask;

  // we need a delay of 4us (then one bit is transfered)
  // this seems more stable then using delayMicroseconds
  asm("nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n");
  asm("nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n");

  asm("nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n");
  asm("nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n");

  asm("nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n");
  asm("nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n");

  for (int i = 0; i < 8; i++)
  {
    if (theByte & 01)
    {
      _SFR_BYTE(_SFR_IO8(portNumber)) |= pinMask;
    }
    else
    {
      _SFR_BYTE(_SFR_IO8(portNumber)) &= ~pinMask;
    }

    asm("nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n");
    asm("nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n");

    asm("nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n");
    asm("nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n");

    asm("nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n");
    asm("nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n");

    theByte >>= 1;
  }

  // the last thing we do is to write the pin to high
  // it will be the mark between bytes. (this break is have to be between 8 us and 1 sec)
  _SFR_BYTE(_SFR_IO8(portNumber)) |= pinMask;

  // reenable interrupts.
  sei();
}

void processDmx() {
        /***** sending the dmx signal *****/

  // sending the break (the break can be between 88us and 1sec)
  digitalWrite(sig, LOW);

  delay(10);

  dmxChannel[0] = 0;

  for (int count = 0; count <= 512; count++)
  {
        if (count < 64)
        shiftDmxOut(sig, dmxChannel[count]);
        else 
                shiftDmxOut(sig,0);
  }

  /***** sending the dmx signal end *****/
}

// fade from current colour to given rgb value
// algorithm by David A. Mellis
void fadeTo(byte r, byte g, byte b) {
        byte i;
        byte p_start[3];
        byte p_end[3];

        p_start[0] = curr_rgb[0];
        p_start[1] = curr_rgb[1];
        p_start[2] = curr_rgb[2];

        p_end[0] = r;   
        p_end[1] = g;   
        p_end[2] = b;

        i = 0;

        while (i < 255) {

                        curr_rgb[0] = (p_end[0] - p_start[0]) / 254 * i + p_start[0];
                        curr_rgb[1] = (p_end[1] - p_start[1]) / 254 * i + p_start[1];
                        curr_rgb[2] = (p_end[2] - p_start[2]) / 254 * i + p_start[2];

                        setDmxChannel(1,  curr_rgb[0]);
                        setDmxChannel(2,  curr_rgb[1]);
                        setDmxChannel(3,  curr_rgb[2]);
                        processDmx();
                        i++;                    
        }

}

void setup()
{
  pinMode(sig, OUTPUT);
}

void loop()
{

  fadeTo(255,0,0);
  delay(500);
  fadeTo(0,255,0);
  delay(500);
  fadeTo(0,0,255);
  delay(500);  

}