xkimi
Published © GPL3+

Affordable Remote Baby Vitals Monitoring/Alert System

Compact Baby Vital Wearable with HR & SPO2 Monitoring & Alert

AdvancedFull instructions provided20 hours2,740

Things used in this project

Hardware components

nRF5340 Development Kit
Nordic Semiconductor nRF5340 Development Kit
×1
Power Profiler Kit
Nordic Semiconductor Power Profiler Kit
×1
SparkFun Pulse Oximeter and Heart Rate Sensor - MAX30101 & MAX32664 (Qwiic)
SparkFun Pulse Oximeter and Heart Rate Sensor - MAX30101 & MAX32664 (Qwiic)
×1
Adafruit DotStar Micro LEDs
×1
MAX30102 High-Sensitivity Pulse Oximeter and Heart-Rate Sensor for Wearable Health
Maxim Integrated MAX30102 High-Sensitivity Pulse Oximeter and Heart-Rate Sensor for Wearable Health
×1

Software apps and online services

VS Code
Microsoft VS Code
EasyEDA
JLCPCB EasyEDA
Fusion 360
Autodesk Fusion 360
Autodesk EAGLE
Autodesk EAGLE
Google Flutter
Zephyr RTOS
Zephyr Project Zephyr RTOS
AWS IoT
Amazon Web Services AWS IoT
AWS SDK
Amazon Web Services AWS SDK
nRF Connect SDK
Nordic Semiconductor nRF Connect SDK

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Hot Air Station, Industrial
Hot Air Station, Industrial
SMD Reflow Oven
Tweezers, SMD Soldering/Desoldering
Tweezers, SMD Soldering/Desoldering

Story

Read more

Custom parts and enclosures

Autodesk360

Schematics

Baby Guard Schematic

Code

Zephyr main.c

C/C++
Zephyr main structure, need driver to function, check github
#include <zephyr/types.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <sys/printk.h>
#include <sys/byteorder.h>
#include <zephyr.h>

#include <drivers/sensor.h>
#include <stdio.h>
#include "max32664.h"

#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/conn.h>
#include <bluetooth/uuid.h>
#include <bluetooth/gatt.h>
#include <bluetooth/services/bas.h>
#include <bluetooth/services/hrs.h>

#include <drivers/led_strip.h>
#include <device.h>
#include <drivers/spi.h>
#include <sys/util.h>

#include "sphr.h"

/* size of stack area used by each thread */
#define STACKSIZE 1024

/* scheduling priority used by each thread */
#define PRIORITY 7

/* delay between greetings (in ms) */
#define SLEEPTIME 500
#define STRIP_NUM_LEDS 1

#define DELAY_TIME K_MSEC(40)

#define COLOR_RED 0
#define COLOR_GREEN 1
#define COLOR_BLUE 2
#define COLOR_BLACK 3
#define COLOR_WHITE 4
#define COLOR_ORANGE 4

uint8_t sphr_data[8];

static const struct led_rgb colors[] = {
	{ .r = 0xff, .g = 0x00, .b = 0x00, }, /* red */
	{ .r = 0x00, .g = 0xff, .b = 0x00, }, /* green */
	{ .r = 0x00, .g = 0x00, .b = 0xff, }, /* blue */
	{ .r = 0x00, .g = 0x00, .b = 0x00, }, /* black */
	{ .r = 0xff, .g = 0xff, .b = 0xff, }, /* white */
	{ .r = 165, .g = 165, .b = 0, }, /* orange */
};

static const struct led_rgb black = {
	.r = 0x00,
	.g = 0x00,
	.b = 0x00,
};

struct led_rgb strip_color;

const struct led_rgb *color_at(size_t time, size_t i)
{
	size_t rgb_start = time % STRIP_NUM_LEDS;

	if (rgb_start <= i && i < rgb_start + ARRAY_SIZE(colors)) {
		return &colors[i - rgb_start];
	} else {
		return &black;
	}
}

K_FIFO_DEFINE(my_fifo);



struct data_item_t {
    void *fifo_reserved;   /* 1st word reserved for use by FIFO */
    int hr;
	int spo2;
	int confidence;
	int status;
};

struct data_item_t tx_data;


static const struct bt_data ad[] = {
	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
	BT_DATA_BYTES(BT_DATA_UUID16_ALL,
		      BT_UUID_16_ENCODE(BT_UUID_HRS_VAL),
		      BT_UUID_16_ENCODE(BT_UUID_BAS_VAL),
		      BT_UUID_16_ENCODE(BT_UUID_DIS_VAL))
};

static void connected(struct bt_conn *conn, uint8_t err)
{
	if (err) {
		printk("Connection failed (err 0x%02x)\n", err);
	} else {
		printk("Connected\n");
	}
}

static void disconnected(struct bt_conn *conn, uint8_t reason)
{
	printk("Disconnected (reason 0x%02x)\n", reason);
}

static struct bt_conn_cb conn_callbacks = {
	.connected = connected,
	.disconnected = disconnected,
};

static void bt_ready(void)
{
	int err;

	printk("Bluetooth initialized\n");

	err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, ad, ARRAY_SIZE(ad), NULL, 0);
	if (err) {
		printk("Advertising failed to start (err %d)\n", err);
		return;
	}

	printk("Advertising successfully started\n");
}

static void auth_cancel(struct bt_conn *conn)
{
	char addr[BT_ADDR_LE_STR_LEN];

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

	printk("Pairing cancelled: %s\n", addr);
}

static struct bt_conn_auth_cb auth_cb_display = {
	.cancel = auth_cancel,
};

static void bas_notify(void)
{
	uint8_t battery_level = bt_bas_get_battery_level();

	battery_level--;

	if (!battery_level) {
		battery_level = 100U;
	}

	bt_bas_set_battery_level(battery_level);
}

static void hrs_notify(void)
{
	static uint8_t heartrate = 90U;

	/* Heartrate measurements simulation */
	heartrate++;
	if (heartrate == 160U) {
		heartrate = 90U;
	}
	//printk("mesaured heart rate=%d\n", heartrate);
	bt_hrs_notify(heartrate);
}

K_THREAD_STACK_DEFINE(threadA_stack_area, STACKSIZE);
static struct k_thread threadA_data;


void threadA(void *dummy1, void *dummy2, void *dummy3)
{
	ARG_UNUSED(dummy1);
	ARG_UNUSED(dummy2);
	ARG_UNUSED(dummy3);

	int err;
	struct sensor_value hr;
	struct sensor_value spo2;
	struct sensor_value status;
	struct sensor_value confidence;
	struct sensor_value temp;
	float tempf = 0;
	uint8_t tempBytes[4];
	//struct sensor_value red;
	//struct sensor_value IR;
	const struct device *dev = device_get_binding(DT_LABEL(DT_INST(0, max_max32664)));

	if (dev == NULL) {
		printf("Could not get max32664 device\n");
		return;
	}

	const struct device *devTemp = device_get_binding(DT_LABEL(DT_INST(0, max_max30205)));

	if (devTemp == NULL) {
		printf("Could not get max30205 device\n");
		return;
	}

	err = bt_enable(NULL);
	if (err) {
		printk("Bluetooth init failed (err %d)\n", err);
		return;
	}





	bt_ready();

	bt_conn_cb_register(&conn_callbacks);
	bt_conn_auth_cb_register(&auth_cb_display);

	/* Implement notification. At the moment there is no suitable way
	 * of starting delayed work so we do it here
	 */

	while (1) {
		k_sleep(K_SECONDS(1));



		sensor_sample_fetch(dev);
		sensor_channel_get(dev, SENSOR_CHAN_HEARTRATE, &hr);
		sensor_channel_get(dev, SENSOR_CHAN_SPO2, &spo2);
		sensor_channel_get(dev, SENSOR_CHAN_HR_STATUS, &status);
		sensor_channel_get(dev, SENSOR_CHAN_CONFIDENCE, &confidence);

		sensor_sample_fetch(devTemp);
		sensor_channel_get(devTemp, SENSOR_CHAN_AMBIENT_TEMP, &temp);
		//sensor_channel_get(dev, SENSOR_CHAN_IR, &IR);
		/* Print green LED data*/
		//printf("red=%d\n", red.val1);
		//printf("IR=%d\n", IR.val1);
		printk("hr=%d\n",hr.val1);
		printk("spo2=%d\n",spo2.val1);
		printk("status=%d\n",status.val1);
		printk("confidence=%d\n",confidence.val1);

		printk("temp1=%d\n",temp.val1);
		printk("temp2=%d\n",temp.val2);

		tempf = sensor_value_to_double(&temp);
		/* Heartrate measurements simulation */
		hrs_notify();
		tx_data.hr = hr.val1;
		tx_data.spo2 = spo2.val1;
		tx_data.status = status.val1;
		tx_data.confidence = confidence.val1;

		k_fifo_put(&my_fifo, &tx_data);
		/* Battery level simulation */
		bas_notify();

		sphr_data[0] = (uint8_t)hr.val1;
		sphr_data[1] = (uint8_t)spo2.val1;
		sphr_data[2] = (uint8_t)status.val1;
		sphr_data[3] = (uint8_t)confidence.val1;

		memcpy(sphr_data + 4, &tempf,sizeof(float));
		
		sphr_notify(sphr_data,8);
	}

}

K_THREAD_STACK_DEFINE(threadB_stack_area, STACKSIZE);
static struct k_thread threadB_data;


void threadB(void *dummy1, void *dummy2, void *dummy3)
{
	ARG_UNUSED(dummy1);
	ARG_UNUSED(dummy2);
	ARG_UNUSED(dummy3);

	struct data_item_t  *rx_data;

	struct data_item_t  *cur_data;

	int minimum_sleep = 100;
	
	int sleep_time = 1000;

	double sleep_timed = 1000;

	const struct device *strip;
	size_t i, time;

	strip = device_get_binding(DT_LABEL(DT_INST(0, apa_apa102)));
	if (strip) {
		printk("Found LED strip device %s", DT_LABEL(DT_INST(0, apa_apa102)));
	} else {
		printk("LED strip device %s not found", DT_LABEL(DT_INST(0, apa_apa102)));
		return;
	}
	
	int cnt = 0;
	while(1) {

		cur_data = k_fifo_get(&my_fifo, K_NO_WAIT);

		if (cur_data)
		{
			rx_data = cur_data;

			if (rx_data->status == 3)
			{
				// get valid hr data, update sleep time
   			    sleep_timed = 1000.0f * 60.0f / (float)rx_data->hr;
				
				sleep_time = (int)sleep_timed;

				if (sleep_time < 200) sleep_time = 200;

				if (sleep_time > 2000) sleep_time = 2000;

				printk("sleep time=%d\n", sleep_time);
			}

		}

		if (rx_data)
		{
			if (rx_data->status == 0)
			{
				strip_color = colors[COLOR_RED];
				led_strip_update_rgb(strip, &strip_color, STRIP_NUM_LEDS);
				k_sleep(K_MSEC(50));
				strip_color = colors[COLOR_BLACK];
				led_strip_update_rgb(strip, &strip_color, STRIP_NUM_LEDS);
				k_sleep(K_MSEC(2000));
			}
			else if (rx_data->status == 1 || rx_data->status == 2)
			{
				strip_color = colors[COLOR_ORANGE];
				led_strip_update_rgb(strip, &strip_color, STRIP_NUM_LEDS);
				k_sleep(K_MSEC(50));
				strip_color = colors[COLOR_BLACK];
				led_strip_update_rgb(strip, &strip_color, STRIP_NUM_LEDS);
				k_sleep(K_MSEC(1000));
			}
			else
			{
				strip_color = colors[COLOR_GREEN];
				led_strip_update_rgb(strip, &strip_color, STRIP_NUM_LEDS);
				k_sleep(K_MSEC(50));
				strip_color = colors[COLOR_BLACK];
				led_strip_update_rgb(strip, &strip_color, STRIP_NUM_LEDS);
				k_sleep(K_MSEC(sleep_time));
			}
			
		}

		k_sleep(K_MSEC(1));
		
	}

}

void main(void)
{


	k_thread_create(&threadA_data, threadA_stack_area,
			K_THREAD_STACK_SIZEOF(threadA_stack_area),
			threadA, NULL, NULL, NULL,
			PRIORITY, 0, K_FOREVER);
	k_thread_name_set(&threadA_data, "thread_a");

	k_thread_start(&threadA_data);


	k_thread_create(&threadB_data, threadB_stack_area,
			K_THREAD_STACK_SIZEOF(threadB_stack_area),
			threadB, NULL, NULL, NULL,
			PRIORITY, 0, K_FOREVER);
	k_thread_name_set(&threadB_data, "thread_b");

	k_thread_start(&threadB_data);
}

Flutter Main code

Dart
Flutter main code, check github for complete source code.
import 'dart:convert';
import 'dart:io';
import 'dart:math';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/services.dart';
import 'dart:async';
import 'package:flutter_blue/flutter_blue.dart';
import 'package:mqtt_client/mqtt_client.dart';
import 'dart:typed_data';

import 'package:mqtt_client/mqtt_server_client.dart';

void main() {
  runApp(MyApp());
}

class HealthData {
  final double value;

  HealthData(this.value);

  Map<String, dynamic> toJson() => {'value': value};
}

class HealthDataString {
  final String value;

  HealthDataString(this.value);

  Map<String, dynamic> toJson() => {'value': value};
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData.dark(),
      home: MyHomePage(title: 'Baby Monitor'),
    );
  }
}

class LineChartDisplay extends StatelessWidget {
  LineChartDisplay({required this.hrInput, required this.sp2Input});

  final List<FlSpot> hrInput;
  final List<FlSpot> sp2Input;
  final List<Color> gradientColors = [
    const Color(0xff23b6e6),
    const Color(0xff02d39a),
  ];

  final List<Color> gradientColors1 = [
    const Color(0xffe623cc),
    const Color(0xffffe67a),
  ];

  double getMinValX(List<FlSpot> d) {
    if (d.isEmpty) return 0;
    double val = d[0].x;

    for (var v in d) {
      if (v.x < val) {
        val = v.x;
      }
    }

    return val;
  }

  double getMinValY(List<FlSpot> d) {
    if (d.isEmpty) return 0;
    double val = d[0].y;

    for (var v in d) {
      if (v.y < val) {
        val = v.y;
      }
    }

    return val;
  }

  double getMaxValY(List<FlSpot> d) {
    print("called");
    if (d.isEmpty) return 0;
    double val = d[0].y;

    for (var v in d) {
      if (v.y > val) {
        val = v.y;
      }
    }
    print('max_y=$val');
    return val;
  }

  double getMaxValX(List<FlSpot> d) {
    if (d.isEmpty) return 0;
    double val = d[0].x;

    for (var v in d) {
      if (v.x > val) {
        val = v.x;
      }
    }

    return val;
  }

  LineChartData mainData(List<FlSpot> hrData, List<FlSpot> sp2Data) {
    return LineChartData(
      gridData: FlGridData(
        show: false,
        drawVerticalLine: true,
        drawHorizontalLine: true,
        checkToShowHorizontalLine: (value) {
          //print(value);
          return true;
        },
        checkToShowVerticalLine: (value) {
          //print(value);
          return true;
        },
        getDrawingHorizontalLine: (value) {
          //print(value);
          return FlLine(
            color: const Color(0xff37434d),
            strokeWidth: 1,
          );
        },
        getDrawingVerticalLine: (value) {
          return FlLine(
            color: const Color(0xff37434d),
            strokeWidth: 1,
          );
        },
      ),
      titlesData: FlTitlesData(
        show: true,
        bottomTitles: SideTitles(
          showTitles: false,
          interval: 2,
          reservedSize: 22,
          getTextStyles: (value) => const TextStyle(
              color: Color(0xff68737d),
              fontWeight: FontWeight.bold,
              fontSize: 16),
          getTitles: (value) {
            return value.toString();
          },
          margin: 8,
        ),
        leftTitles: SideTitles(
          showTitles: true,
          interval: 10,
          getTextStyles: (value) => const TextStyle(
            color: Color(0xff67727d),
            fontWeight: FontWeight.bold,
            fontSize: 15,
          ),
          getTitles: (value) {
            //print(value)
            return value.toString();
          },
          reservedSize: 22,
          margin: 20,
        ),
      ),
      borderData: FlBorderData(
          show: true,
          border: Border.all(color: const Color(0xff37434d), width: 1)),
      minX: getMinValX(hrData),
      maxX: getMaxValX(hrData),
      minY: 40,
      maxY: 150,
      lineBarsData: [
        LineChartBarData(
          spots: hrData,
          isCurved: true,
          colors: gradientColors,
          barWidth: 2,
          isStrokeCapRound: true,
          dotData: FlDotData(
            show: false,
          ),
          belowBarData: BarAreaData(
            show: false,
            colors:
                gradientColors.map((color) => color.withOpacity(0.3)).toList(),
          ),
        ),
        LineChartBarData(
          spots: sp2Data,
          isCurved: true,
          colors: gradientColors1,
          barWidth: 2,
          isStrokeCapRound: true,
          dotData: FlDotData(
            show: false,
          ),
          belowBarData: BarAreaData(
            show: false,
            colors:
                gradientColors1.map((color) => color.withOpacity(0.3)).toList(),
          ),
        ),
      ],
    );
  }

  @override
  Widget build(BuildContext context) {
    return AspectRatio(
      aspectRatio: 1.23,
      child: Container(
        decoration: const BoxDecoration(
          borderRadius: BorderRadius.all(Radius.circular(18)),
          gradient: LinearGradient(
            colors: [
              Color(0xff2c274c),
              Color(0xff46426c),
            ],
            begin: Alignment.bottomCenter,
            end: Alignment.topCenter,
          ),
        ),
        child: Stack(
          children: <Widget>[
            Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: <Widget>[
                const SizedBox(
                  height: 4,
                ),
                const Text(
                  'Heart Rate - SPO2 Chart',
                  style: TextStyle(
                      color: Colors.white,
                      fontSize: 28,
                      fontWeight: FontWeight.bold,
                      letterSpacing: 2),
                  textAlign: TextAlign.center,
                ),
                const SizedBox(
                  height: 37,
                ),
                Expanded(
                  child: Padding(
                    padding: const EdgeInsets.only(right: 16.0, left: 20.0),
                    child: LineChart(
                      mainData(hrInput, sp2Input),
                      swapAnimationDuration: const Duration(milliseconds: 250),
                    ),
                  ),
                ),
                const SizedBox(
                  height: 10,
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, required this.title}) : super(key: key);

  // This widget is the home page of your application. It is stateful, meaning
  // that it has a State object (defined below) that contains fields that affect
  // how it looks.

  // This class is the configuration for the state. It holds the values (in this
  // case the title) provided by the parent (in this case the App widget) and
  // used by the build method of the State. Fields in a Widget subclass are
  // always marked "final".

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class AwsMQTT {
  late MqttServerClient client;

  Future<void> init() async {
    print("init aws mqtt");

    ByteData caData = await rootBundle.load('certs/AmazonRootCA.pem');
    ByteData certData =
        await rootBundle.load('certs/awsiot-certificate.pem.crt');
    ByteData privateKeyData =
        await rootBundle.load('certs/awsiot-private.pem.key');

    var _iotEndpoint = 'a3e4d2huogr3x6-ats.iot.us-east-1.amazonaws.com';
    var _clientId = '123123';

    client = MqttServerClient.withPort(_iotEndpoint, _clientId, 8883,
        maxConnectionAttempts: 5);
    client.logging(on: true);
    client.keepAlivePeriod = 20;
    client.secure = true;

    final securityContext = SecurityContext.defaultContext;

    securityContext.useCertificateChainBytes(certData.buffer.asUint8List());
    securityContext.setClientAuthoritiesBytes(caData.buffer.asUint8List());
    securityContext.usePrivateKeyBytes(privateKeyData.buffer.asUint8List());

    client.securityContext = securityContext;
    client.setProtocolV311();

    // final MqttConnectMessage connMess = MqttConnectMessage()
    //     .withClientIdentifier(_clientId)
    //     .startClean()
    //     .keepAliveFor(30);

    // client.connectionMessage = connMess;

    try {
      print('MQTTClientWrapper::Mosquitto client connecting....');
      await client.connect();
    } on Exception catch (e) {
      print('MQTTClientWrapper::client exception - $e');
      client.disconnect();
    }
  }

  awsMQTT() {}

  Future<void> publish(String topic, String msg) async {
    if (client.connectionStatus!.state == MqttConnectionState.connected) {
      final builder1 = MqttClientPayloadBuilder();

      builder1.addString(msg);

      client.publishMessage(topic, MqttQos.atLeastOnce, builder1.payload!);
    } else {
      print(
          'MQTTClientWrapper::ERROR Mosquitto client connection failed - disconnecting, status is ${client.connectionStatus}');
      client.disconnect();
    }
  }

  void uninit() {
    client.disconnect();
  }
}

class _MyHomePageState extends State<MyHomePage> {
  int _hr = 0;
  int _sp2 = 0;
  int _sensorStatus = 0;
  int _confidence = 0;
  int _counter = 0;
  double _temp = 0;
  FlutterBlue flutterBlue = FlutterBlue.instance;
  late BluetoothDevice bleDevice;
  bool isFound = false;
  List<FlSpot> hrData = [];
  List<FlSpot> sp2Data = [];
  late Timer _timer;
  var rng = new Random();
  String _strBleStatus = 'N/A';
  String _strSensorStatus = 'N/A';
  int isInit = 0;
  AwsMQTT awsClient = new AwsMQTT();

  Future<int> publishStatus(String status) async {
    awsClient.publish(
        'babyAlert/string/sensorStatus', jsonEncode(HealthDataString(status)));

    return 0;
  }

  Future<int> publishTemp(double temp) async {
    awsClient.publish(
        'babyAlert/double/bodyTemp', jsonEncode(HealthData(temp.toDouble())));

    return 0;
  }

  Future<int> publishData(
      int hr, int spo2, int sensorStatus, int confidence) async {
    awsClient.publish(
        'babyAlert/int/hr', jsonEncode(HealthData(hr.toDouble())));

    awsClient.publish(
        'babyAlert/int/spo2', jsonEncode(HealthData(spo2.toDouble())));

    awsClient.publish('babyAlert/int/status',
        jsonEncode(HealthData(sensorStatus.toDouble())));

    awsClient.publish(
        'babyAlert/int/conf', jsonEncode(HealthData(confidence.toDouble())));

    return 0;
  }

  Future<void> connectDevice(BluetoothDevice dev) async {
    await dev.connect();
    print("connected");
    List<BluetoothService> services = await dev.discoverServices();
    services.forEach((service) {
      print('service=${service.uuid.toString()}');
      if (service.uuid.toString() == '12345678-1234-5678-1234-56789abcdef0') {
        print("found heart rate service");

        for (var char in service.characteristics) {
          print('char=${char.uuid.toString()}');
          if (char.uuid.toString() == '12345678-1234-5678-1234-56789abcdef1') {
            print("found characteristic");
            char.setNotifyValue(true).whenComplete(() {
              char.value.listen((event) {
                if (event.length == 8) {
                  print('heart rate = ${event[0]}');
                  print('spo2 = ${event[1]}');
                  print('status = ${event[2]}');
                  print('confidence = ${event[3]}');

                  ByteData fdata = new ByteData(4);

                  fdata.setUint8(0, event[7]);

                  fdata.setUint8(1, event[6]);
                  fdata.setUint8(2, event[5]);
                  fdata.setUint8(3, event[4]);

                  _temp = fdata.getFloat32(0);

                  print('temp = $_temp');

                  publishTemp(_temp);

                  setState(() {
                    _hr = event[0];
                    _sp2 = event[1];
                    _sensorStatus = event[2];
                    _confidence = event[3];

                    _strBleStatus = 'Connected';

                    switch (_sensorStatus) {
                      case 0:
                        _strSensorStatus = 'No Contact';
                        break;
                      case 1:
                        _strSensorStatus = 'Contact';
                        break;
                      case 2:
                      case 3:
                        _strSensorStatus = 'Stable';
                        break;
                    }

                    publishStatus(_strSensorStatus);

                    if (_sensorStatus == 3 &&
                        _hr > 40 &&
                        _hr < 180 &&
                        _sp2 > 80 &&
                        _sp2 <= 100) {
                      // valid data
                      _counter++;
                      var hrspot =
                          new FlSpot(_counter.toDouble(), _hr.toDouble());
                      hrData.add(hrspot);
                      var sp2spot =
                          new FlSpot(_counter.toDouble(), _sp2.toDouble());

                      sp2Data.add(sp2spot);

                      publishData(_hr, _sp2, _sensorStatus, _confidence);
                    }
                  });
                }
              });
            });
          }
        }
      }
      // do something with service
    });
  }

  @override
  initState() {
    super.initState();

    // _timer = new Timer.periodic(Duration(seconds: 1), (timer) {
    //   setState(() {
    //     _counter++;
    //     _hr = rng.nextDouble() * 50 + 50;
    //     var hr = new FlSpot(_counter.toDouble(), _hr);
    //     if (hrData.length > 20) {
    //       hrData.removeAt(0);
    //     }

    //     hrData.add(hr);

    //     _sp2 = rng.nextDouble() * 10 + 90;
    //     var sp2 = new FlSpot(_counter.toDouble(), _sp2);
    //     if (sp2Data.length > 20) {
    //       sp2Data.removeAt(0);
    //     }

    //     sp2Data.add(sp2);
    //   });
    // });
    //
    isFound = false;

    setState(() {
      _strBleStatus = 'Scanning';
    });

    flutterBlue.startScan(timeout: Duration(seconds: 5)).whenComplete(() => {
          if (isFound)
            awsClient.init().whenComplete(() => connectDevice(bleDevice))
        });

    flutterBlue.scanResults.listen((results) {
      // do something with scan results
      for (ScanResult r in results) {
        print('device=${r.device.id.id}');
        if (r.device.id.id == 'EC:E3:26:B6:EA:A0') {
          print("found device");
          setState(() {
            _strBleStatus = 'Found Device';
          });
          bleDevice = r.device;
          isFound = true;

          break;
        }
      }
    });
  }

  @override
  void dispose() {
    super.dispose();
    bleDevice.disconnect();
    awsClient.uninit();
    print("disposed1");
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Padding(
              padding: const EdgeInsets.only(
                  right: 18.0, left: 12.0, top: 24, bottom: 12),
              child: LineChartDisplay(hrInput: hrData, sp2Input: sp2Data),
            ),
            Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
              Column(
                children: [
                  Text(
                    'Heart Rate',
                    style: TextStyle(fontSize: 18),
                  ),
                  Text(
                    _hr.toStringAsFixed(0),
                    style: Theme.of(context).textTheme.headline2,
                  ),
                ],
              ),
              Column(
                children: [
                  Text(
                    'Oxygen',
                    style: TextStyle(fontSize: 18),
                  ),
                  Text(
                    _sp2.toStringAsFixed(0),
                    style: Theme.of(context).textTheme.headline2,
                  ),
                ],
              ),
              Column(
                children: [
                  Text(
                    'Body Temp',
                    style: TextStyle(fontSize: 18),
                  ),
                  Text(
                    _temp.toStringAsFixed(1),
                    style: Theme.of(context).textTheme.headline2,
                  ),
                ],
              )
            ]),
            Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
              Column(
                children: [
                  Text(
                    'Sensor Status:',
                    style: TextStyle(fontSize: 15),
                  ),
                  Text(
                    _strSensorStatus,
                    style: Theme.of(context).textTheme.headline4,
                  )
                ],
              ),
              Column(
                children: [
                  Text(
                    'BLE Status:',
                    style: TextStyle(fontSize: 15),
                  ),
                  Text(
                    _strBleStatus,
                    style: Theme.of(context).textTheme.headline4,
                  )
                ],
              )
            ])
          ],
        ),
      ),
    );
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(DiagnosticsProperty<Timer>('_timer', _timer));
  }
}

nrf5340dk overlay file

ActionScript
&i2c1 {
	status = "okay";
	sda-gpios = <&gpio0 30 0>;
	scl-gpios = <&gpio0 31 0>;
    max32664@55{
        status = "okay";
        compatible = "max,max32664";
        reg = < 0x55 >;
        label = "MAX32664";
        rst-gpios = <&gpio0 13 GPIO_ACTIVE_HIGH>;
        mfio-gpios = <&gpio1 9 GPIO_PULL_UP>;
    };

    max30205@48{
        status = "okay";
        compatible = "max,max30205";
        reg = < 0x48 >;
        label = "MAX30205";
    };
};

&arduino_spi {
    status = "okay";
	apa102@0 {
		compatible = "apa,apa102";
		reg = <0>;
		spi-max-frequency = <5250000>;
		label = "APA102";
	};
};

Baby Guard Github Repo

Contains Zephy code, Flutter Code, Schematics

Credits

xkimi

xkimi

3 projects • 8 followers

Comments

Add projectSign up / Login