Mithun Das
Published © GPL3+

Buddy - A personal home office assistance

Buddy collects temperature, humidity, light, human presence data, calculates productivity, helps break sedentary, built on AWS Edukit.

IntermediateFull instructions providedOver 1 day507
Buddy - A personal home office assistance

Things used in this project

Hardware components

AWS IoT EduKit
Amazon Web Services AWS IoT EduKit
×1
Grove - Temperature, Humidity, Pressure and Gas Sensor (BME680)
Seeed Grove - Temperature, Humidity, Pressure and Gas Sensor (BME680)
×1
SparkFun Human Presence Sensor Breakout - AK9753
×1
Grove - Light Sensor
Seeed Grove - Light Sensor
×1
I2C Hub 1 to 6 Expansion TCA9548A Module
M5Stack I2C Hub 1 to 6 Expansion TCA9548A Module
×1

Software apps and online services

AWS IoT
Amazon Web Services AWS IoT
AWS Cognito
Amazon Web Services AWS Cognito
AWS Lambda
Amazon Web Services AWS Lambda
AWS DynamoDB
Amazon Web Services AWS DynamoDB
Amazon Web Services AWS Pinpoint
Amazon Web Services AWS Amplify
Arduino IDE
Arduino IDE
Xcode
Apple Xcode
VS Code
Microsoft VS Code

Story

Read more

Code

M5Stack.ino

Arduino
#include <M5Core2.h>
#include "FastLED.h"
#include <ArduinoECCX08.h>
#include <Wire.h>
#include "Adafruit_BME680.h"
#include "WiFi.h"
#include <bsec.h>
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
#include <BLE2902.h>
#include "ClosedCube_TCA9548A.h"
//#include "images.h"
#include <driver/i2s.h>
#include "Grove_Human_Presence_Sensor.h"

#define BLE_INTERVAL_IN_SECONDS 120
#define SPRITE_COLOR_DEPTH 16
#define LIGHT_SENSOR 36

TFT_eSprite spr = TFT_eSprite(&M5.Lcd);

//app icons
extern const unsigned char previewR[120264];
extern const unsigned char logo[];
extern const unsigned char temperature_icon[];
extern const unsigned char humidity_icon[];
extern const unsigned char iaq_icon[];
extern const unsigned char bat_icon[];
extern const unsigned char light_icon[];
extern const unsigned char bell_icon[];

#define CONFIG_I2S_BCK_PIN 12 //定义I2S相关端口
#define CONFIG_I2S_LRCK_PIN 0
#define CONFIG_I2S_DATA_PIN 2
#define CONFIG_I2S_DATA_IN_PIN 34

#define Speak_I2S_NUMBER I2S_NUM_0  //定义扬声器端口

#define MODE_MIC 0  //定义工作模式
#define MODE_SPK 1
#define DATA_SIZE 1024

bool InitI2SSpeakOrMic(int mode){  //Init I2S.  初始化I2S
    esp_err_t err = ESP_OK;

    i2s_driver_uninstall(Speak_I2S_NUMBER); // Uninstall the I2S driver.  卸载I2S驱动
    i2s_config_t i2s_config = {
        .mode = (i2s_mode_t)(I2S_MODE_MASTER),  // Set the I2S operating mode.  设置I2S工作模式
        .sample_rate = 44100, // Set the I2S sampling rate.  设置I2S采样率
        .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, // Fixed 12-bit stereo MSB.  固定为12位立体声MSB
        .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT, // Set the channel format.  设置频道格式
        .communication_format = I2S_COMM_FORMAT_I2S,  // Set the format of the communication.  设置通讯格式
        .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, // Set the interrupt flag.  设置中断的标志
        .dma_buf_count = 2, //DMA buffer count.  DMA缓冲区计数
        .dma_buf_len = 128, //DMA buffer length.  DMA缓冲区长度
    };
    if (mode == MODE_MIC){
        i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM);
    }else{
        i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX);
        i2s_config.use_apll = false;  //I2S clock setup.  I2S时钟设置
        i2s_config.tx_desc_auto_clear = true; // Enables auto-cleanup descriptors for understreams.  开启欠流自动清除描述符
    }
    // Install and drive I2S.  安装并驱动I2S
    err += i2s_driver_install(Speak_I2S_NUMBER, &i2s_config, 0, NULL);

    i2s_pin_config_t tx_pin_config;
    tx_pin_config.bck_io_num = CONFIG_I2S_BCK_PIN;  // Link the BCK to the CONFIG_I2S_BCK_PIN pin. 将BCK链接至CONFIG_I2S_BCK_PIN引脚
    tx_pin_config.ws_io_num = CONFIG_I2S_LRCK_PIN;  //          ...
    tx_pin_config.data_out_num = CONFIG_I2S_DATA_PIN;  //       ...
    tx_pin_config.data_in_num = CONFIG_I2S_DATA_IN_PIN; //      ...
    err += i2s_set_pin(Speak_I2S_NUMBER, &tx_pin_config); // Set the I2S pin number.  设置I2S引脚编号
    err += i2s_set_clk(Speak_I2S_NUMBER, 44100, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_MONO); // Set the clock and bitwidth used by I2S Rx and Tx. 设置I2S RX、Tx使用的时钟和位宽
    return true;
}

void SpeakInit(void){ // 初始化扬声器
  M5.Axp.SetSpkEnable(true);  //启用扬声器电源
  InitI2SSpeakOrMic(MODE_SPK);
}

void DingDong(void){
  size_t bytes_written = 0;
  i2s_write(Speak_I2S_NUMBER, previewR, 120264, &bytes_written, portMAX_DELAY);
}


AK9753 movementSensor;

// need to adjust these sensitivities lower if you want to detect more far
// but will introduce error detection
float sensitivity_presence = 4.0;
float sensitivity_movement = 4.0;
int detect_interval = 30; //milliseconds
PresenceDetector detector(movementSensor, sensitivity_presence, sensitivity_movement, detect_interval);

uint32_t last_time;
uint32_t last_ble_send;
uint8_t last_ble_move = 0;

ClosedCube::Wired::TCA9548A tca9548a;

Bsec iaqSensor;
String output;
String mac;

void checkIaqSensorStatus(void);

int t = 0, h = 0, a = 0, p = 0, s = 0, avg = 0;

#define PaHub_I2C_ADDRESS  0x70
#define BME680_DEFAULT_ADDRESS (0x76)   //for Grove , use 0x77 for Sparkfun
#define BLENAME "M5StackCore2"
#define SERVICE_UUID                  "4D7D1101-BA27-40B2-836C-17505C1044D7"
#define TX_CHAR_UUID                  "4D7D1102-BA27-40B2-836C-17505C1044D7"
#define RX_CHAR_UUID                  "4D7D1103-BA27-40B2-836C-17505C1044D7"

BLEServer* pServer = NULL;
BLECharacteristic* pTxCharacteristic = NULL;
BLECharacteristic* pRxCharacteristic = NULL;
bool bleConnected = false;

WiFiClient client;

#define LEDS_PIN 25
#define LEDS_NUM 10
CRGB ledsBuff[LEDS_NUM];
Adafruit_BME680 bme680; // I2C



void checkIaqSensorStatus(void)
{
  if (iaqSensor.status != BSEC_OK) {
    if (iaqSensor.status < BSEC_OK) {
      output = "BSEC error code : " + String(iaqSensor.status);
      Serial.println(output);
      while (true) {

      }

    } else {
      output = "BSEC warning code : " + String(iaqSensor.status);
      Serial.println(output);
    }
  }

  if (iaqSensor.bme680Status != BME680_OK) {
    if (iaqSensor.bme680Status < BME680_OK) {
      output = "BME680 error code : " + String(iaqSensor.bme680Status);
      Serial.println(output);
      while (true) {

      }
    } else {
      output = "BME680 warning code : " + String(iaqSensor.bme680Status);
      Serial.println(output);
    }
  }
}


class M5StackBLEServerCallbacks: public BLEServerCallbacks {
    void onConnect(BLEServer* pServer) {
      bleConnected = true;
      setLEDBarColor(0, 0, 225);

    };

    void onDisconnect(BLEServer* pServer) {
      bleConnected = false;
      setLEDBarColor(255, 255, 255);
    }
};

class CharacteristicCallbacks: public BLECharacteristicCallbacks {
    void onWrite(BLECharacteristic *pCharacteristic) {
      std::string rxValue = pCharacteristic->getValue();

      if (rxValue.length() > 0) {

        setLEDBarColor(0, 225, 0);
        delay(3000);
        setLEDBarColor(0, 0, 225);
        
        Serial.println("*********");
        Serial.print("Received Value: ");
        for (int i = 0; i < rxValue.length(); i++)
          Serial.print(rxValue[i]);

        Serial.println();
        Serial.println("*********");
      }
    }
};

void setupBLEServer() {
  BLEDevice::init(BLENAME);
  pServer = BLEDevice::createServer();
  pServer->setCallbacks(new M5StackBLEServerCallbacks());

  // Create the BLE Service
  BLEService *pService = pServer->createService(SERVICE_UUID);

  // Create a BLE Characteristic
  pTxCharacteristic = pService->createCharacteristic(
                        TX_CHAR_UUID,
                        BLECharacteristic::PROPERTY_READ   |
                        BLECharacteristic::PROPERTY_WRITE  |
                        BLECharacteristic::PROPERTY_NOTIFY
                      );
  pTxCharacteristic->addDescriptor(new BLE2902());

  pTxCharacteristic->setValue("00;00;00;00");

  pRxCharacteristic = pService->createCharacteristic(
                        RX_CHAR_UUID,
                        BLECharacteristic::PROPERTY_READ   |
                        BLECharacteristic::PROPERTY_WRITE  |
                        BLECharacteristic::PROPERTY_NOTIFY
                      );
  pRxCharacteristic->setCallbacks(new CharacteristicCallbacks());

  pService->start();

  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->addServiceUUID(SERVICE_UUID);
  pAdvertising->setScanResponse(true);
  pAdvertising->setMinPreferred(0x06);  // functions that help with iPhone connections issue
  pAdvertising->setMinPreferred(0x12);

  //pAdvertising->setMinPreferred(0x0);

  BLEAdvertisementData adv;
  adv.setName(BLENAME);
  pAdvertising->setAdvertisementData(adv);

  BLEAdvertisementData adv2;
  adv2.setCompleteServices(BLEUUID(SERVICE_UUID));
  pAdvertising->setScanResponseData(adv2);

  BLEDevice::startAdvertising();
  mac = BLEDevice::getAddress().toString().c_str();
  

}
void setup() {
  M5.begin();
  pinMode(LIGHT_SENSOR, INPUT);
  Wire.begin();
  tca9548a.address(PaHub_I2C_ADDRESS);

  FastLED.addLeds<SK6812, LEDS_PIN>(ledsBuff, LEDS_NUM);
  setLEDBarColor(255, 255, 255);
  M5.Lcd.setTextColor(RED);
  M5.Lcd.setTextSize(3);
  //display full screen logo
  M5.Lcd.pushImage(0, 0, 320, 240, (uint8_t *)logo);

  tca9548a.selectChannel(0);
  iaqSensor.begin(BME680_I2C_ADDR_PRIMARY, Wire);
  checkIaqSensorStatus();
  bsec_virtual_sensor_t sensorList[10] = {
    BSEC_OUTPUT_RAW_TEMPERATURE,
    BSEC_OUTPUT_RAW_PRESSURE,
    BSEC_OUTPUT_RAW_HUMIDITY,
    BSEC_OUTPUT_RAW_GAS,
    BSEC_OUTPUT_IAQ,
    BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE,
    BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY,
  };

  iaqSensor.updateSubscription(sensorList, 7, BSEC_SAMPLE_RATE_LP);
  checkIaqSensorStatus();
  delay(5000);
  //Initializing AK9753 sensor
  tca9548a.selectChannel(1);

  if (movementSensor.initialize() == false) {
    Serial.println("Device not found. Check wiring.");
    while (1);
  }

  last_time = millis();
  last_ble_send = millis();
  last_ble_move = 0;

  
  delay(1000);

  setupBLEServer();

  //M5.Lcd.pushImage(0, 0, 50, 50, (uint8_t *)logo16_glcd_bmp);

  SpeakInit();
  DingDong();
  delay(100);

  //delay(10000);

  M5.Lcd.clear(BLACK);
  M5.Lcd.setCursor(0, 0);
  M5.Lcd.setTextSize(2);

//setup display screen for once
  M5.Lcd.drawFastVLine(105,0,240,TFT_LIGHTGREY);
  M5.Lcd.drawFastVLine(210,0,240,TFT_LIGHTGREY);
  M5.Lcd.drawFastHLine(0,120,320,TFT_LIGHTGREY);

  M5.Lcd.pushImage(10, 10, 32, 32, (uint8_t *)temperature_icon);
  M5.Lcd.pushImage(125, 10, 32, 32, (uint8_t *)humidity_icon);
  M5.Lcd.pushImage(240, 10, 32, 32, (uint8_t *)iaq_icon);
  M5.Lcd.pushImage(10, 130, 32, 32, (uint8_t *)bat_icon);
  M5.Lcd.pushImage(125, 130, 32, 32, (uint8_t *)light_icon);
  M5.Lcd.pushImage(240, 130, 32, 32, (uint8_t *)bell_icon);
  
  delay(1000);
}

void setLEDBarColor(uint32_t r, uint32_t g, uint32_t b) {
  FastLED.setBrightness(10);
  for (int i = 0; i < LEDS_NUM; i++)
  {
    ledsBuff[i].setRGB(g, r, b);
  }
  FastLED.show();
}


void loop() {
  M5.update();

  // if (M5.BtnA.wasReleased() || M5.BtnA.pressedFor(1000, 200)) {
  //   DingDong();
  // }
    if(M5.Touch.ispressed()) {
      TouchPoint_t coordinate = M5.Touch.getPressPoint();
      Serial.printf("x:%d, y:%d \r\n", coordinate.x, coordinate.y);
      if (coordinate.x > 210 && coordinate.y > 180){
        //bell sprite tapped
        DingDong();
      }
      
    }  

  //M5.Axp.SetLed(1);
  tca9548a.selectChannel(1);
  detector.loop();
  uint32_t now = millis();
  if (now - last_time > 100) {
    

    // M5.Lcd.clear(BLACK);
    // M5.Lcd.setCursor(0, 0);
    // M5.Lcd.setTextSize(3);

    int light = analogRead(LIGHT_SENSOR);
    int lightPct = map(light,0,4095,0,100);
    //M5.Lcd.setTextColor(GREEN);
    //M5.Lcd.printf("MAC: %s\n", mac.c_str());
    //M5.Lcd.printf("LIGHT: %d\n", light);
    float batVol = M5.Axp.GetBatVoltage(); // 4.16
    int bVol = (int)(batVol * 100.0);
    //M5.Lcd.printf("BAT: %d\n", bVol);
    int bPct = 100;
    if(batVol >= 4.100){
        bPct = 100;
    } else if(batVol >= 3.95){
        bPct = 75;
    } else if(batVol >= 3.80){
        bPct = 50;
    } else if(batVol >= 3.25){
        bPct = 25;
    } else{
        bPct = 0;
    }

   uint8_t move = 0;  // default value if not movement detected

    if (detector.presentField1()) {
      //M5.Lcd.setTextColor(GREEN);
      Serial.println("IR1: X");
      move = 1;
    } else {
      // M5.Lcd.setTextColor(RED);
      // M5.Lcd.println("IR1: 0");
    }

    if (detector.presentField2()) {
      // M5.Lcd.setTextColor(GREEN);
      Serial.println("IR2: X");
      move = 1;
    } else {
      // M5.Lcd.setTextColor(RED);
      // M5.Lcd.println("IR2: 0");
    }

    if (detector.presentField3()) {
      // M5.Lcd.setTextColor(GREEN);
      Serial.println("IR3: X");
      move = 1;
    } else {
      // M5.Lcd.setTextColor(RED);
      //Serial.println("IR3: 0");
    }

    if (detector.presentField4()) {
      // M5.Lcd.setTextColor(GREEN);
      Serial.println("IR4: X");
      move = 1;
    } else {
      // M5.Lcd.setTextColor(RED);
      // M5.Lcd.println("IR4: 0");
    }
    
    // uint8_t m = detector.getMovement();  //read movement state will clear it
    // //M5.Lcd.setTextColor(WHITE);
    // //plot a pulse for movement between 1-3
    // if (m & MOVEMENT_FROM_1_TO_3) {
    //   //M5.Lcd.println("1 TO 3");
    //   move = 1; //movement left to right
    //   Serial.println("Movement");
      
    // } else if (m & MOVEMENT_FROM_3_TO_1) {
    //   //M5.Lcd.println("3 TO 1");
    //   move = 2; //movement right to left
    //   Serial.println("Movement");
     
    // } else {
    //   //M5.Lcd.println("NA");
    // }

    // //Serial.print(detector.getDerivativeOfDiff24());
    // //Serial.print(" ");

    // //plot a pulse for movement between 2-4
    // if (m & MOVEMENT_FROM_2_TO_4) {
    //   //M5.Lcd.println("2 TO 4");
    //   move = 3;  // up to down
    //   Serial.println("Movement");
     
    // } else if (m & MOVEMENT_FROM_4_TO_2) {
    //   //M5.Lcd.println("4 TO 2");
    //   move = 4; //down to up
    //   Serial.println("Movement");
      
    // } else {
    //   //M5.Lcd.println("NA");
    // }

    if( move != 0 ){
      Serial.printf("Setting move  to 1 for value %d\n", move);
      last_ble_move = 1;
    }

    tca9548a.selectChannel(0);
    if (iaqSensor.run()) { // If new data is available

      t = (int) iaqSensor.temperature * 1.8 + 32;
      h =  iaqSensor.humidity;
      a = iaqSensor.iaq;
      p = (int) (iaqSensor.pressure / 100);

      
      spr.createSprite(100, 50);
      spr.fillSprite(TFT_BLACK);
      spr.setTextColor(TFT_LIGHTGREY);
      spr.setTextSize(3);
      spr.drawNumber(t, 5, 5);
      spr.drawString("F", 45, 5);
      spr.pushSprite(0, 50);
      spr.deleteSprite();

      //humidity 
      spr.setColorDepth(SPRITE_COLOR_DEPTH);
      spr.createSprite(103, 70);
      
      if(h > 70){
        spr.fillSprite(TFT_RED);
      }else{
        spr.fillSprite(TFT_BLACK);
      }
      
      spr.setTextColor(TFT_LIGHTGREY);
      spr.setTextSize(3);
      spr.drawNumber(h, 15, 15);
      spr.drawString("%", 60, 15);
      spr.pushSprite(106, 50);
      spr.deleteSprite();
      

      spr.createSprite(80, 50);
      spr.fillSprite(TFT_BLACK);
      spr.setTextColor(TFT_LIGHTGREY);
      spr.setTextSize(3);
      spr.drawNumber(a, 5, 5);
      //spr.drawString("%", 45, 5);
      spr.pushSprite(230, 50);
      spr.deleteSprite();

      spr.createSprite(80, 50);
      spr.fillSprite(TFT_BLACK);
      spr.setTextColor(TFT_LIGHTGREY);
      spr.setTextSize(3);
      spr.drawNumber(bPct, 5, 5);
      spr.drawString("%", 65, 5);
      spr.pushSprite(0, 170);
      spr.deleteSprite();


      //light 
      spr.createSprite(103, 70);
      
      if(lightPct < 50){
        spr.fillSprite(TFT_RED);
      }else{
        spr.fillSprite(TFT_BLACK);
      }
      
      spr.setTextColor(TFT_LIGHTGREY);
      spr.setTextSize(3);
      spr.drawNumber(lightPct, 15, 15);
      spr.drawString("%", 60, 15);
      spr.pushSprite(106, 170);
      spr.deleteSprite();

      //bell 
      spr.createSprite(103, 70);
      
      if(light > 70){
        spr.fillSprite(TFT_GREEN);
        spr.setTextColor(TFT_BLACK);
      }else{
        spr.fillSprite(TFT_BLACK);
        spr.setTextColor(TFT_LIGHTGREY);
      }
      
      
      spr.setTextSize(5);
      spr.drawNumber(0, 30, 15);
      spr.pushSprite(211, 170);
      spr.deleteSprite();


      delay(1000);


      if ( bleConnected && (now -last_ble_send > BLE_INTERVAL_IN_SECONDS  * 1000 )) {
        char buf[72];
        sprintf(buf, "%s;%d;%d;%d;%d;%d;%d", mac.c_str(), t, h, a, lightPct,last_ble_move,bPct);
        Serial.println(String(buf).c_str());
        pTxCharacteristic->setValue(String(buf).c_str());

        pTxCharacteristic->notify();
        last_ble_send = now;
        last_ble_move = 0;
      }


    } else {
      checkIaqSensorStatus();
    }

    last_time = now;
  }

  delay(1);
  



}

Code

Credits

Mithun Das

Mithun Das

23 projects • 106 followers
Hacker and Maker driven by passion. Ambassador at Edge Impulse and Balena. Follow me on Twitter @tweetmithund

Comments

Add projectSign up / Login