serge-45
Published © GPL3+

Connecting BlueBus Technology photocells to Arduino

Let Arduino pilot photocells and other home automation sensors connected via the BlueBus Technology.

IntermediateShowcase (no instructions)549
Connecting BlueBus Technology photocells to Arduino

Things used in this project

Hardware components

Arduino UNO
Arduino UNO
×1
Arduino Mega 2560
Arduino Mega 2560
×1
Breadboard (generic)
Breadboard (generic)
×1
Resistor 1k ohm
Resistor 1k ohm
×1
Resistor 10k ohm
Resistor 10k ohm
×1
MOSFET Transistor, Switching
MOSFET Transistor, Switching
×1
Resistor 4.7 ohm
×1
General Purpose Transistor PNP
General Purpose Transistor PNP
×1
Test Accessory, AC Power Adaptor
Test Accessory, AC Power Adaptor
×1

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Schematics

Schematics of the small hardware piece

Code

bluebus.ino

C/C++
INO file to open in the Arduino IDE
/* Project: BlueBus
 * Date   : 2022-10-31
 * Author : Serge
 */

//============================================================================================
// BB module
//============================================================================================

// OC2B, on MEGA2560 : PH6 = D9
// OC2B, on UNO : PD3 = D3
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
#define OC2B        9  /* Arduino Mega */
#elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__)
#define OC2B        14 /* Sanguino */
#else
#define OC2B        3  /* Arduino Duemilanove, Diecimila, LilyPad, etc */
#endif

// digitalWrite(OC2B, LOW);  => MOSFET blocked => both bus lines pulled up to +Vin => 0V on the bus (active signal)
#define BB_ACTIVE LOW  // MOSFET blocked

// digitalWrite(OC2B, HIGH); => MOSFET conducting => 1 bus line connected to GND => Vin on the Bus (idle)
#define BB_IDLE HIGH   // MOSFET conducting

byte BB_in;                 // port number for reading the bus
volatile byte BB_reco = 0;  // trigger recognition phase
byte BB_addr;               // current addr being scanned

#define BB_EVENTS 32        // max number of events waiting in the queue
typedef struct {
  byte evt;
  unsigned long timestamp;
  byte addr;
  byte status;
} BB_Event;
volatile BB_Event BB_events[BB_EVENTS];  // event queue
BB_Event BB_event;                       // one event
volatile byte BB_event_in = 0;           // index of next event to write to the queue
volatile byte BB_event_out = 0;          // index of next event to read from the queue

// presence of devices on the bus, updated by the recognition phase
byte BB_device[16] = {
  false, true,  false, false, false, false, false, false, // device #1 is always scanned
  false, false, false, false, false, false, false, false,
};

// last status of each device (0xff = no answer)
byte BB_status[16] = {
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
};

void BB_init(byte in);    // initialize and use port "in" to read the bus status
void BB_on();             // activate the bus, querying devices
void BB_idle();           // power the bus, but don't query any device
void BB_off();            // power off the bus
void BB_recognition();    // initiate the recognition phase
BB_Event* BB_event_pop(); // get next event
void BB_event_push(byte reply);   // add new event to queue (called by ISR)

// called by ISR in case of status change on a device
void BB_event_push(byte reply) {
  byte status;

  if (reply==0 || reply==0xff) {
    status = 0xff;
  } else {
    status = ((reply ^ (reply>>4) ^ BB_addr) & 0b1111);
    if (!BB_device[BB_addr]) {
      BB_device[BB_addr] = true;
      BB_status[BB_addr] = 0xff;
    }
  }
  if (status != BB_status[BB_addr]) {
    volatile BB_Event* e;
    BB_status[BB_addr] = status;
    e = &BB_events[BB_event_in];
    e->evt = true;
    e->timestamp = micros();
    e->addr = BB_addr;
    e->status = status;
    if (BB_event_in>=BB_EVENTS-1) {
      BB_event_in = 0;
    } else {
      BB_event_in++;
    }
    if (BB_event_in == BB_event_out) {
      BB_event_pop();
    }
  }
}

// get next event from the queue
BB_Event* BB_event_pop() {
  volatile BB_Event* out;
  if (BB_event_in == BB_event_out) {
    BB_event.evt = false;
  } else {
    out = &BB_events[BB_event_out];
    BB_event.evt = out->evt;
    BB_event.timestamp = out->timestamp;
    BB_event.addr = out->addr;
    BB_event.status = out->status;
    if (BB_event_out>=BB_EVENTS-1) {
      BB_event_out = 0;
    } else {
      BB_event_out++;
    }
  }
  return &BB_event;
}

void BB_init(byte in) {

  BB_in = in; // port used for reading the bus
  pinMode(OC2B, OUTPUT);
  pinMode(BB_in, INPUT);

  // no power on the bus
  BB_off();

  // CPU clock is 16MHz (MEGA2560)
  // CS2 = 0b101 = clock divided by 128
  // TCNT incremented every 8µs (128/16M = 8µ)
  TCCR2B &= 0xff ^ (_BV(CS22) | _BV(CS21) | _BV(CS20)) ;
  TCCR2B |= _BV(CS22) | _BV(CS20) ;

  // EXCLK = 0, internal clock
  ASSR &= 0xff ^ _BV(EXCLK);

  // AS2 = 0, asynchrone mode
  ASSR &= 0xff ^ _BV(AS2);

  // COM2A = COM2B = 00 : normal port operation
  TCCR2A &= 0xff ^ (_BV(COM2A1) | _BV(COM2A0) | _BV(COM2B1) | _BV(COM2B0));

  // COM2B = 11 : inverted output (LOW at start, HIGH when OCR2B is reached
#define BB_ACTIVATE_SIGNAL() TCCR2A |= _BV(COM2B1) | _BV(COM2B0);

  // COM2B = 00 : normal port operation
#define BB_DEACTIVATE_SIGNAL() TCCR2A &= 0xff ^ (_BV(COM2B1) | _BV(COM2B0));
  BB_DEACTIVATE_SIGNAL();

  // WGM2 = 0b111 => fast PWM mode, TOP=OCRA
  TCCR2A |= (_BV(WGM21) | _BV(WGM20));
  TCCR2B |= _BV(WGM22);

// |------------32ms-------------|  |------------19-------|
// |xxxxxxxx0-------16.9ms-------|  |xxxxxxxx0-------10---|
// |xxxxxxxx0yyyyyyyy0---1.8ms---|  |xxxxxxxx0yyyyyyyy0-1-|
//
// 19-bit frame (8 bits + stop + 8 bits + stop + 1 idle bit) lasting 32ms => 1.68ms/bit => 210 ticks of 8µs
// very little delay measured before the start of the first bit of yyyyyyyy, about 0.3ms (210/6=35 ticks)

#define BB_FRAME 210
  OCR2A = BB_FRAME;

//                 _0.56_                 __70__
// 1  |___1.1ms___|      |     |___140___|      |
//
//            ___1.1ms___              ___140___
// 0  |_0.56_|           |     |__70__|         |
//             ^                        ^
// read      __|                      __|
//
// Bit 0 transmitted as 1/3 LOW + 2/3 HIGH
#define BB_BIT0 (BB_FRAME/3)
// Bit 1 transmitted as 2/3 LOW + 1/3 HIGH
#define BB_BIT1 ((BB_FRAME/3)*2)
// middle of a bit for reading its state (+estimated delay for reply to start)
#define BB_BITR (BB_FRAME/2 + BB_FRAME/6)
  OCR2B = BB_BITR;

}

#define PARITY(x) (((x) + ((x)>>1) + ((x)>>2) + ((x)>>3) + ((x)>>4) + ((x)>>5) + ((x)>>6) + ((x)>>7)) & 0b1)

// called by ISR to get next BB_addr to query, in recognition phase and in norml operation phase
byte BB_sequence() {
  static byte addr = 0;
  static byte reco_count = 0;
  byte byteQ;
  byte flag;

  // normal operation, query each recognized device
  if (BB_reco==0) {
    flag = 1;
    do { 
      addr = (addr+1) & 0b1111;
    }
    while (!BB_device[addr]);
  }

  // start recognition phase, starting with addr #1
  if (BB_reco==1) {
    addr = 1;
    reco_count = 2;
    BB_reco++;
  }
  
  // recognition phase running, test twice each address from 1 to 15
  if (BB_reco>1) {
    flag = 0;
    if (reco_count) {
      reco_count--;
    } else {
      addr++;
      reco_count = 2;
      if (addr>15) {
        BB_reco = 0;
        addr = 0;
      }
    }
  }

  // data to send : aabcccfp
  //    aa : address extension (0 ?)
  //    b  : address modifier (A bridge on the board)
  //    ccc : addr as configured with the jumpers
  //    f : reset flag ? should be 1 for normal operation
  //    p : parity bit
  BB_addr = addr;
  byteQ = (addr<<2) | (flag<<1);
  // set parity bit
  byteQ ^= PARITY(byteQ);
  return byteQ;
}

// Interrupt service routine triggered when TIMER2 reach OCR2B value
ISR(TIMER2_COMPB_vect) {
  static byte bit_count = 0;
  static byte data = 0;

  // ========== send the query ==========
  // activate timer signal
  if (bit_count==0) {
    data = BB_sequence();
    BB_ACTIVATE_SIGNAL();
  }

  // Q 8bits + stop bit (zero)
  if (bit_count<9) {
    OCR2B = (data & _BV(7)) ? BB_BIT1 : BB_BIT0;
    data <<= 1;
  }

  // end of byteQ, deactivate
  if (bit_count==9) {
    BB_DEACTIVATE_SIGNAL();
    OCR2B = BB_BITR;
    data = 0;
  }

  // ========== read the status ==========
  // start to read 8 bits on **next** cycle
  if (bit_count>=10 && bit_count<18) {
    data = (data<<1) + (digitalRead(BB_in) ? 0 : 1);
  }

  // stop bit (should be zero)
  if (bit_count==18) {
    BB_event_push(data);
  }

  // one frame (32ms) = 9+9+1 bits
  if (bit_count<18) {
    bit_count++;
  } else {
    bit_count = 0;
  }
}

void BB_on() {
  
  // Interrupt on Comparator B only: OCIE2B=1, OCIE2A=0, TOIE2=0
  TIMSK2 &= 0xff ^ (_BV(OCIE2A) | _BV(OCIE2B) | _BV(TOIE2));
  TIMSK2 |= _BV(OCIE2B);
  // bus powered
  digitalWrite(OC2B, BB_IDLE);
}

void BB_idle() {
  
  // No interrupt: OCIE2B=0, OCIE2A=0, TOIE2=0
  TIMSK2 &= 0xff ^ (_BV(OCIE2A) | _BV(OCIE2B) | _BV(TOIE2));
  // no query but bus still powered
  digitalWrite(OC2B, BB_IDLE);

}

void BB_off() {
  
  // No interrupt: OCIE2B=0, OCIE2A=0, TOIE2=0
  TIMSK2 &= 0xff ^ (_BV(OCIE2A) | _BV(OCIE2B) | _BV(TOIE2));
  // no power on the bus
  digitalWrite(OC2B, BB_ACTIVE);

}

// initiate recognition phase. It will last about 15*2*32ms = 1 second
void BB_recognition() {
  BB_reco = 1;
}

//============================================================================================

void setup() {                      // setup(): put your setup code here, to run once:

  Serial.begin(2e6);
  Serial.println("\n\n\n\nINIT...");

  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);

  BB_init(3);       // read bus on port D3
  BB_on();          // activate bus and communication
  delay(1000);      // wait for the devices to power-up
  BB_recognition(); // initiate a recognition phase

  digitalWrite(LED_BUILTIN, LOW);
  Serial.println("INIT... Completed!");
}

void loop() {                       // loop(): put your main code here, to run repeatedly:

  BB_Event* e;

  while ((e=BB_event_pop())->evt) {
    Serial.print("Device ");
    Serial.print(e->addr, HEX);
    Serial.print(", status ");
    Serial.print(e->status, HEX);
    Serial.print(", timestamp ");
    Serial.println(e->timestamp);
    digitalWrite(LED_BUILTIN, e->status & 0b0010 ? HIGH : LOW);
  }
  delay(200);
}

Credits

serge-45
0 projects • 0 followers

Comments