1. It really takes some time to complete the project, in prototype below.

Most of the efforts spend on Architecture of Design until I am notified the time is ticking.
Using Schedule Table and separated code blocks, I can put many ideas to be tested and improved continuously. This can only achieve merely basic functions. There are more works to do to make it useful platform for the proposed  BWaC protocol.

2. Here is the wiring before applying power on the USB port.

3. I would explain some main blocks and work to be done later.

3.1 Full code list below,

#include "BWaC.h"


#include <Servo.h>


#include <SPI.h>
#include <WiFi101.h>
#define PubNub_BASE_CLIENT WiFiClient
#include <PubNub.h>


#include <arduinoFFT.h>
#include <ScheduleTable.h>


arduinoFFT arFFT = arduinoFFT(); /* Create FFT object */
const uint16_t samples = 128; //This value MUST ALWAYS be a power of 2. Attention: Max value 512 for XMC1100 with 12208 Byte RAM. 1024 for MKR1000
#define SAMPLINGFREQUENCY 4000
#define CYCLETIME  250  // 1000ms/SAMPLINGFREQUENCY =250us
/* These are the input and output vectors Input vectors receive computed results from FFT */
double vReal[samples];
double vImag[samples];
#define SCL_INDEX 0x00
#define SCL_TIME 0x01
#define SCL_FREQUENCY 0x02
#define SCL_PLOT 0x03
// Define the number of samples value to determine the size of the readings array.
const int numReadings = samples; 
int readings[numReadings];      // the readings from the analog input
unsigned long timeFFT,timeADC;








static char ssid[] = "hornor";   // your network SSID (name)
static char pass[] = "aiitxf96";  // your network password
int status = WL_IDLE_STATUS;                // the Wifi radio's status
const static char pubkey[] = "demo";         // your publish key 
const static char subkey[] = "demo";         // your subscribe key
const static char channel[] = "bwac"; // channel to use
char msg[] = "\"MKR1000 message Head!\"";


SchedTable<1> firstCycle_BW_IDLE(1000);
SchedTable<1> secondCycle_BW_ACTIVE(500);
SchedTable<1> thirdCycle(200);
SchedTable<1> forthCycle(200);
SchedTable<1> fifthCycle_FFT(500);
SchedTable<1> sixCycle_PUBLISH(500);


//
enum { RESERVED, DOORLOCK, CURTAIN_A,CURTAIN_B,LIGHT_A,LIGHT_B,LIGHT_C,LIGHT_D};
enum { BW_IDLE,BW_ACTIVE, BWAC_CTL, BWAC_CMD,FFT,PUBLISH, LPM0, LPM1,};
byte pBWAC=0x00;
byte pState=0;
byte cBWAC=0x00;
byte cState;
byte state;


#define THRESHHOLD  50
#define TARGETFRQ  150  // TargetFrequency !!!
#define WS_GAP  200  // Whistle On gap, now 200ms
unsigned long Whistl_ON;
bool WauthPass;
double maxFrq;
unsigned long timeWhistleOK,timeWhistleOFF,timeWhistleChange,timeWhistleCount;
byte WS_CMD_tagNm=0;
byte WS_CMD_mode=0;


Servo doorLock;  // create a servo object
int MIC_inputPin = A0;


void init_ScheduleTable(){
  firstCycle_BW_IDLE.at(0,firstDT);
  secondCycle_BW_ACTIVE.at(0,secondWS);
  thirdCycle.at(0,thirdCTL);
  forthCycle.at(0,forthCMD);
  fifthCycle_FFT.at(0,fifthFFT);
  sixCycle_PUBLISH.at(0,sixthPUB);
}
  
void stop_ScheduleTable(){
  firstCycle_BW_IDLE.stop(); 
  secondCycle_BW_ACTIVE.stop(); 
  thirdCycle.stop(); 
  forthCycle.stop(); 
  fifthCycle_FFT.stop(); 
  sixCycle_PUBLISH.stop(); 
}


void firstDT()   {
  Serial.println("firstCycle.");
  bool BW_Detected;
  int MIC_in;
  //BW_Detection... Serial.println(MIC_in); 
  MIC_in=analogRead(MIC_inputPin);
  BW_Detected=( analogRead(MIC_inputPin)> THRESHHOLD); 
   //BW_Detected= 0;  
  if  (BW_Detected){  
    cState=BW_ACTIVE;}
  else {
         bitClear(cBWAC, BW_ACTIVE);
         secondCycle_BW_ACTIVE.stop(); 
         cState=BW_IDLE;}    
  ;          
  }


void secondWS()   {
    Serial.println("2ndCycle."); 
    Whistl_ON = millis();
    cState=FFT; // BITSET 5thCycle
}




void BW_CTL()   {
   if ( WauthPass==false) {
   timeWhistleCount=millis();
   timeWhistleChange=0;
   }
   if ( WauthPass==true) {
        timeWhistleChange=timeWhistleCount-millis();
   }
   if ( timeWhistleChange>WS_GAP) {
    WS_CMD_tagNm++;
    WS_CMD_tagNm=WS_CMD_tagNm- (int(WS_CMD_tagNm/8))*8;
    delay(1000);  
   }
    // If timeWhistleChange > 3 times of WS_GAP, toggle the WS_CMD_mode,
   if ( timeWhistleChange>(3*WS_GAP)) {  
    WS_CMD_mode=bitRead(pBWAC,WS_CMD_tagNm);  
    WS_CMD_mode=1-WS_CMD_mode;
    if ( WS_CMD_mode==1) {  
    bitSet(pBWAC,WS_CMD_mode);
    }
    else if (( WS_CMD_mode==0)){
      bitClear(pBWAC,WS_CMD_mode);
    }
         
    delay(1000);  
   } 
  
}  


void W_auth()   {
  if ( (abs(maxFrq-TARGETFRQ))<50 ) {
    BW_CTL();
    WauthPass= true;     
    
    }
    else {
      BW_CTL();
      WauthPass=false; 
    }
}




void thirdCTL()   {
  Serial.println("3Cycle."); 
  W_auth()  ;
  if  (WauthPass== true ) {
    bitClear(cBWAC, BWAC_CTL);  
    thirdCycle.stop(); 
      cState=BWAC_CMD;
     }
    else {
          bitClear(cBWAC, BWAC_CTL);  
          thirdCycle.stop(); 
          cState=BW_ACTIVE;      
    }
  
   // BITSET 4thCycle
}


void BW_CMD()   {
   byte tg_mode, tg_Nm;
   tg_Nm=WS_CMD_tagNm;
   tg_mode=bitRead(pBWAC,tg_Nm);
   pState=tg_Nm;
   
   switch (pState) {
      case RESERVED:
      // Do nothing.
      break;


      case DOORLOCK:
        int angle;
        if (tg_mode==1){
          angle=10;//open 
          }
        else if (tg_mode==1){
          angle=160;//close
          }          
        doorLock.write(angle); 
        delay(1000); // wait for the servo to get there      
      break;
      
      case CURTAIN_A:
        if (true){
        digitalWrite(CURTAIN_A, HIGH);      
        digitalWrite(CURTAIN_B, LOW);
        }
        delay(1000); // Mortor run for 1m to open curtain    
        digitalWrite(CURTAIN_A, LOW);      
        digitalWrite(CURTAIN_B, LOW);     
      
      break;
      
      case CURTAIN_B:
        if (true){
        digitalWrite(CURTAIN_B, HIGH);      
        digitalWrite(CURTAIN_A, LOW);
        }
        delay(1000); // Mortor run for 1m to open curtain    
        digitalWrite(CURTAIN_A, LOW);      
        digitalWrite(CURTAIN_B, LOW);     
      break;
      
      case LIGHT_A:
      digitalWrite(LIGHT_A, tg_mode); 
      break;
      
      case LIGHT_B:
      digitalWrite(LIGHT_B, tg_mode);      
      break;
      
      case LIGHT_C:
      digitalWrite(LIGHT_C, tg_mode);      
      break;
      
      case LIGHT_D:
      digitalWrite(LIGHT_D, tg_mode); 
      break;
   
      
      default:
      // statements
      break;
  }
  
  
}  
   
void forthCMD()   {
  Serial.println("4Cycle."); 
  BW_CMD();
  bitClear(cBWAC, BWAC_CMD); 
  forthCycle.stop(); 
    cState=PUBLISH; // BITSET 6thCycle
  
}


void init_FFT(){
    // initialize FFT and all the readings to 0:
  for (uint16_t i = 0; i < samples; i++){  
    vReal[i] = 0.0; vImag[i] = 0.0; 
    }
  for (int thisReading = 0; thisReading < numReadings; thisReading++) {
    readings[thisReading] = 0;
    }
}


void adcTune(double *vReal,double *vImag ,int *readings)
{
  for (int  readIndex= 0; readIndex < numReadings; readIndex++){
    delayMicroseconds(250); // Op Time of 55ms 
    readings[readIndex] = analogRead(MIC_inputPin);  }   
  for (int i = 0; i < numReadings; i++) {
      vReal[i] = readings[i] ;
      vImag[i] = 0.0;  }
}


void fftTune(double *vReal,double *vImag, uint16_t samples){
        arFFT.Windowing(vReal, samples, FFT_WIN_TYP_HAMMING, FFT_FORWARD);  /* Weigh data */
        arFFT.Compute(vReal, vImag, samples, FFT_FORWARD); /* Compute FFT */
        arFFT.ComplexToMagnitude(vReal, vImag, samples); /* Compute magnitudes */
        //Serial.println("Computed magnitudes:");
        //PrintVector(vReal, (samples >> 1), SCL_FREQUENCY);
        double x = arFFT.MajorPeak(vReal, samples, SAMPLINGFREQUENCY);
         Serial.print("Major Peak:"); Serial.println(x, 6);
         maxFrq=x;
         
}
void fifthFFT(){
  Serial.println("5Cycle."); 
        init_FFT();
        timeFFT=millis(); 
        adcTune(vReal,vImag, readings);
        Serial.print(millis()-timeFFT); Serial.println("ms of ADC times. ");
        //timeFFT=millis(); 
        fftTune(vReal,vImag, samples);
        //Serial.print(millis()-timeFFT); Serial.println("ms of FFT times. ");
        //delay(200);  
  bitClear(cBWAC, FFT); 
  fifthCycle_FFT.stop(); 
    cState=BWAC_CTL; // BITSET 3rdCycle      
    //Serial.print("cState="); Serial.println(cState); 
}


void initPUB()   {
    // attempt to connect using WPA2 encryption:
  Serial.println("Attempting to connect to WPA network...");
  status = WiFi.begin(ssid, pass);


  // if you're not connected, stop here:
  if ( status != WL_CONNECTED) {
    Serial.println("Couldn't get a wifi connection");
    while (true);
  } else {
    Serial.print("WiFi connecting to SSID: ");
    Serial.println(ssid);


    PubNub.begin(pubkey, subkey);
    Serial.println("PubNub set up");
  }
}




void pb_PUBLISHING()   {
    /* Publish */
  
  WiFiClient *client;
  
  char msg[] = "\"MKR1000 message!\"";


  client = PubNub.publish(channel, msg);


  if (!client) {
    Serial.println("publishing error");
    delay(1000);
    return;
  }
  if (PubNub.get_last_http_status_code_class() != PubNub::http_scc_success) {
    Serial.print("Got HTTP status code error from PubNub, class: ");
    Serial.print(PubNub.get_last_http_status_code_class(), DEC);
  }
  while (client->available()) {
    Serial.write(client->read());
  }
  client->stop();
  Serial.println("---");
}


void sixthPUB()   {
  Serial.println("6Cycle."); 
  pb_PUBLISHING() ;
  bitClear(cBWAC, PUBLISH); 
  sixCycle_PUBLISH.stop(); 
}    


void initPins(){
  #define RESERVED 6  // Digital D6 pins
  pinMode(RESERVED, OUTPUT);
  digitalWrite(RESERVED, LOW); 
    #define DOORLOCK 7  // Digital D7 pins
  doorLock.attach(DOORLOCK);
    #define CURTAIN_A 8  // Digital D6 pins
  pinMode(CURTAIN_A, OUTPUT);
  digitalWrite(CURTAIN_A, LOW); 
    #define CURTAIN_B 9  // Digital D7 pins
  pinMode(CURTAIN_B, OUTPUT);
  digitalWrite(CURTAIN_B, LOW); 
      #define LIGHT_A 10  // Digital D6 pins
  pinMode(LIGHT_A, OUTPUT);
  digitalWrite(LIGHT_A, LOW); 
    #define LIGHT_B 11  // Digital D7 pins
  pinMode(LIGHT_B, OUTPUT);
    digitalWrite(LIGHT_B, LOW); 
      #define LIGHT_C 12  // Digital D6 pins
  pinMode(LIGHT_C, OUTPUT);
  digitalWrite(LIGHT_C, LOW); 
    #define LIGHT_D 13  // Digital D7 pins
  pinMode(LIGHT_D, OUTPUT);
  digitalWrite(LIGHT_D, LOW);   
      #define TRIG  A2  // Digital D7 pins
  pinMode(TRIG, OUTPUT);
  digitalWrite(TRIG, LOW);   
}
  
void setup()
{
   Serial.begin(115200);   
   while (!Serial) {} // Wait for Serial to be Ready.
   // Init...
   initPins();
   initPUB() ;
   init_ScheduleTable();
   firstCycle_BW_IDLE.start();  
   
}


void loop()
{
    state=cState; 
    switch (state) {
      case BW_IDLE:  
         bitSet(cBWAC, BW_IDLE);
         //firstCycle_BW_IDLE.start(); // Never stopped!
         cState=LPM0;
      break;
      
      case BW_ACTIVE: 
        bitSet(cBWAC, BW_ACTIVE);
        secondCycle_BW_ACTIVE.start(); 
        cState=LPM0;


      break;
    
      case BWAC_CTL:    
        bitSet(cBWAC, BW_ACTIVE); 
                thirdCycle.start(); 
                //Serial.println("BWAC_CTL."); 
        cState=LPM0; 
      break;


      case BWAC_CMD:  
        bitSet(cBWAC, BWAC_CMD);
                forthCycle.start(); 
        cState=LPM0;
      break;
      
      case FFT: 
        bitSet(cBWAC, FFT);
                fifthCycle_FFT.start(1); 
        cState=LPM0;
      break;
    
      case PUBLISH: 
        bitSet(cBWAC, PUBLISH); 
                sixCycle_PUBLISH.start(2); 
        cState=LPM0;
      break;
      }
  
  ScheduleTable::update();   /* update all the schedule tables at once */        
  }






/*****************To Be deleted afterward****************/
void PrintVector(double *vData, uint16_t bufferSize, uint8_t scaleType)
{
  for (uint16_t i = 0; i < bufferSize; i++)
  {
    double abscissa;
    /* Print abscissa value */
    switch (scaleType)
    {
      case SCL_INDEX:
        abscissa = (i * 1.0);
  break;
      case SCL_TIME:
        abscissa = ((i * 1.0) / SAMPLINGFREQUENCY);
  break;
      case SCL_FREQUENCY:
        abscissa = ((i * 1.0 * SAMPLINGFREQUENCY) / samples);
  break;
    }
    Serial.print(abscissa, 6);
    if(scaleType==SCL_FREQUENCY)
      Serial.print("Hz");
    Serial.print(" ");
    Serial.println(vData[i], 4);
  }
  //Serial.println();
}

 

3.2  To include the sketch in one file, too many global variables are used, especially cBWAC and pBWAC. This will be hard to understood if not well documented. I will improve with parameters in functions later.

As to the thread-like schedule table, I have test in BLOG #9, it is good to use. Only problems is the minimal time-cycle is 1 ms. If you need more accurate operation, this will distort your program order. In fact, I have random order or Cycle 3 and Cycle 4 during test. But it is good, since the best part of the schedule table is that it can run according what happened, the logical shall be independent from your manual adjustment.

 

3.2 The Analogue Reading of Sound from Microphone Module have been tested well. The arduinoFFT library can  perform good calculation. But in this design, I have touched the maximum performance value of MKR1000.

First, the maximum sample frequency is 1kHz, since the time need to perform analogue reading shall takes about 0.3ms unless Assembly Language shall be used to access register directly.

In short, the whistle with maximum frequency of 2kHz need the sample frequency of at least 4kHz, just as what I defined with 1000ms/SAMPLINGFREQUENCY =250us

#define CYCLETIME  250

But the result is not accurate.

Second, the maximum sample value of Analogue data is 1024, or the memory overflows. That means the accuracy of sample shall be

SAMPLINGFREQUENCY/ samples = 31.25Hz.

Therefore, I need newly-built whistle below 1kHz for better performance and accuracy.

 

3.3 The Control Command Generation

In  the following code, Control command is defined and authenticated,

void BW_CTL() 
void W_auth() 


The authentication condition in this code  is,

(abs(maxFrq-TARGETFRQ))<50

Featured frequency of the whistle shall be calculated first before put into use. This is not good experience. The whistle shall be tested and recorded for future application. In fact, I am satisfied with my complex Code Structure, since I can build new routine in function void W_auth() only, without touch other part of the code if proper define "WauthPass= true".

Same applies to void BW_CTL() too´╝îit authenticate whistle duration is longer than WS_GAP,

if (timeWhistleChange>WS_GAP)

Then control target move to next one. This is the routing of polling under control. And if it is longer than 3 time of WS_GAP,

if ( timeWhistleChange>(3*WS_GAP)) 

The control is active to toggle the mode. For Light, the status change from On to OFF or OFF to ON. Ditto to curtain control and DoorLock control of Close/Open.

This is simplest Arithmetic Logical, OK but far from good. I have figured out better choice, using controls like MORSE code in dot-dash order. That is new coding system. Still, it can be done within  void BW_CTL() as well.

 

3.4 There is nothing new to doorLock control, the servo turn in 90 degree to open the lock or close the lock thanks to the Servo library, as simple as

doorLock.write(angle); 

 

3.5 For curtain open/close, turn the brush-less motor clockwise or counter-clockwise. Make duration

delay(1000); // Mortor run for 1m to open curtain  

as the distance to travel. It is better to use sonic-distance sensor HC-SR04 to fix the Distance Ranger with ease , below for reference.

#include<UltraDistSensor.h>
UltraDistSensor mysensor;
float reading;
void setup() {
    Serial.begin(9600);
    mysensor.attach(12,13);//Trigger pin , Echo pin
    reading=mysensor.distanceInCm();
}


void loop() {

For the HC-SR04, 5V shall be applied. It can not run in 3.3V as the datasheet claimed. I have got continuously 0.1CM feedback until I figure out what happens.

 

4. For now, the prototype design can run with what I had meant to do. While there are still two parts missing.

  • I have prepared the draft Android Application receiving Published message from MKR1000, supervising the status of Light, Doorlock, Curtains and other extended sensor value.
  • I have prepared data for Machine Leaning model, using Neurona library. This is can be used for Whistle Blow Authentication and multi-decoding process.

There are still many improvements needed to be achieved for the project. I will make more try.