Pass RR in BLE Protocol [Heart Rate] for Arduino - c

code:
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
#include <BLE2902.h>
byte flags = 0b00011110;
byte bpm;
byte rr;
byte heart[8] = { 0b00011110, 60, 60, 60, 60 , 60, 60, 60};
byte hrmPos[1] = {2};
bool _BLEClientConnected = false;
#define heartRateService BLEUUID((uint16_t)0x180D)
BLECharacteristic heartRateMeasurementCharacteristics(BLEUUID((uint16_t)0x2A37), BLECharacteristic::PROPERTY_NOTIFY);
BLECharacteristic sensorPositionCharacteristic(BLEUUID((uint16_t)0x2A38), BLECharacteristic::PROPERTY_READ);
BLEDescriptor heartRateDescriptor(BLEUUID((uint16_t)0x2901));
BLEDescriptor sensorPositionDescriptor(BLEUUID((uint16_t)0x2901));
class MyServerCallbacks : public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
_BLEClientConnected = true;
};
void onDisconnect(BLEServer* pServer) {
_BLEClientConnected = false;
}
};
void InitBLE() {
BLEDevice::init("Sensor X");
// Create the BLE Server
BLEServer *pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
// Create the BLE Service
BLEService *pHeart = pServer->createService(heartRateService);
pHeart->addCharacteristic(&heartRateMeasurementCharacteristics);
heartRateMeasurementCharacteristics.addDescriptor(&heartRateDescriptor);
heartRateMeasurementCharacteristics.addDescriptor(new BLE2902());
pHeart->addCharacteristic(&sensorPositionCharacteristic);
sensorPositionCharacteristic.addDescriptor(&sensorPositionDescriptor);
pServer->getAdvertising()->addServiceUUID(heartRateService);
pHeart->start();
// Start advertising
pServer->getAdvertising()->start();
}
void setup() {
Serial.begin(115200);
Serial.println("Start");
InitBLE();
bpm = 1;
rr = 800;
}
void loop() {
// put your main code here, to run repeatedly:
heart[1] = (byte)bpm;
int energyUsed = 3000;
heart[3] = energyUsed / 256;
heart[6] = (byte)bpm;
heart[2] = energyUsed - (heart[2] * 256);
Serial.println(bpm);
Serial.println(rr);
heartRateMeasurementCharacteristics.setValue(heart, 8);
heartRateMeasurementCharacteristics.notify();
sensorPositionCharacteristic.setValue(hrmPos, 1);
rr++;
bpm++;
delay(2000);
}
I have the above code - and I have been looking at this here but I can't figure out how do I pass the RR data over BLE. I set the flags according to the BLE spec shown here. But whenever I use an app to see the data "HRV Logger", it says that I am not transmitted RR data. I thought all I had to do was pass heart[6] data and that would show up as RR. I have dummy information being passed since I'm not connected to any sensors. Help would be helpful, determining how to pass the RR data across, thanks! (for future reference I am using the Sparkfun ESP32, but this shouldn't affect solving the issue since it has to do with protocols)

Related

Stream sine wave to XAudio2

I am trying to write a very simple sine wave generator that plays out through XAudio2.
Currently there is sound playing, and if I call Win32XAudioInit() and then Win32PlayTone() a tone will play, and the tone will change on subsequent calls to Win32PlayTone(), however there is a noticeable click almost every time the tone changes.
I know there are a few reasons that could cause this:
I am not keeping track of the phase-offset, which means new waves would be misaligned.
I am simply updating the Memory that the buffer is pointing to without regard to what is playing.
Regarding #2, I am not sure if XAudio wants me to create a new XAUDIO2_BUFFER and resubmit that every time I change the tone, or if I am supposed to somehow keep track of where the 'playhead' is (for lack of a better term) and only update bytes that have already been played.
I know if #2 is a problem, I won't be able to hear if I fixed it I am still plagued by problem #1.
I have read through XAudio2 - Play generated sine, when changing frequency clicking sound and I think I could figure out the sin wave problem if I knew XAudio2 was set up correctly.
Any thoughts would be helpful, thanks!
struct win32_audio_buffer
{
real32 Memory[44100 * 1]; // samples per buffer (44100) * channels 1
int BytesPerBuffer;
XAUDIO2_BUFFER XBuffer;
IXAudio2 *XEngine;
IXAudio2SourceVoice *SourceVoice;
WAVEFORMATEX WaveFormat;
};
// NOTE XAUDIO2
internal HRESULT
Win32XAudioInit(win32_audio_buffer *AudioBuffer)
{
// Initialize a COM:
HRESULT HRes;
HRes = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if(FAILED(HRes)) { return(HRes); }
// Init XAUDIO Engine
AudioBuffer->XEngine = {};
if (FAILED(HRes = XAudio2Create(&AudioBuffer->XEngine, 0, XAUDIO2_DEFAULT_PROCESSOR)))
{ return HRes; }
// MASTER VOICE
IXAudio2MasteringVoice* XAudioMasterVoice = nullptr;
if (FAILED(HRes = AudioBuffer->XEngine->CreateMasteringVoice(&XAudioMasterVoice)))
{ return HRes; }
AudioBuffer->WaveFormat = {};
//int32 SamplesPerBuffer = 4410;
int SampleHz = 44100;
WORD Channels = 1;
WORD BitsPerChannel = 32; // 4 byte samples
int32 BufferSize = Channels * BitsPerChannel * SampleHz;
AudioBuffer->BytesPerBuffer = SampleHz * Channels;
AudioBuffer->WaveFormat.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; // or could use WAVE_FORMAT_PCM WAVE_FORMAT_IEEE_FLOAT
AudioBuffer->WaveFormat.nChannels = Channels;
AudioBuffer->WaveFormat.nSamplesPerSec = SampleHz;
AudioBuffer->WaveFormat.wBitsPerSample = BitsPerChannel; // 32
AudioBuffer->WaveFormat.nBlockAlign = (Channels * BitsPerChannel) / 8;
AudioBuffer->WaveFormat.nAvgBytesPerSec = SampleHz * Channels * BitsPerChannel / 8;
AudioBuffer->WaveFormat.cbSize = 0; // set to zero for PCM or IEEE float
AudioBuffer->XBuffer.Flags = 0;
AudioBuffer->XBuffer.AudioBytes = SampleHz * Channels * BitsPerChannel / 8;
AudioBuffer->XBuffer.PlayBegin = 0;
AudioBuffer->XBuffer.PlayLength = 0;
AudioBuffer->XBuffer.LoopBegin = 0;
AudioBuffer->XBuffer.LoopLength = 0;
AudioBuffer->XBuffer.LoopCount = XAUDIO2_LOOP_INFINITE;
AudioBuffer->XBuffer.pContext = NULL;
AudioBuffer->XBuffer.pAudioData = (BYTE *)&AudioBuffer->Memory;
if(FAILED(HRes = AudioBuffer->XEngine->CreateSourceVoice(&AudioBuffer->SourceVoice, (WAVEFORMATEX*)&AudioBuffer->WaveFormat)))
{ return HRes; }
if(FAILED(HRes = AudioBuffer->SourceVoice->Start(0)))
{ return HRes; }
if(FAILED(HRes = AudioBuffer->SourceVoice->SubmitSourceBuffer(&AudioBuffer->XBuffer)))
{ return HRes; }
return(S_OK);
}
internal HRESULT
Win32PlayTone(win32_audio_buffer *Buffer, int32 Hz)
{
real32 PI2 = (real32)6.28318; //530718;
for(int i = 0;
i < Buffer->BytesPerBuffer;
i++)
{
real32 CurrentSample = sinf(i * PI2 / 44100 * Hz);
Buffer->Memory[i] = CurrentSample;
}
return(S_OK);
}
XAudio2 is entirely asynchronous, so you should not change the memory pointed to by a playing packet until the packet is completed or you will get clicks as you describe.
Also, when the current packet completes, you want to have another one already queued up if you want the sound to be continuous.
See these resources for learning how to program XAudio2:
https://github.com/walbourn/directx-sdk-samples/tree/master/XAudio2
https://github.com/microsoft/Xbox-ATG-Samples/tree/master/UWPSamples/Audio
DirectX Tool Kit for Audio

Compile error for AI Thinker ESP32-CAM card

Hello I am a beginner of C language for microprocessors. I want to copy a .bmp file using copyimage() function .This error occurs when compile button is pushed:
#include "esp_camera.h"
#include "Arduino.h"
#include "FS.h" // SD Card ESP32
#include "SD_MMC.h" // SD Card ESP32
#include "soc/soc.h" // Disable brownour problems
#include "soc/rtc_cntl_reg.h" // Disable brownour problems
#include "driver/rtc_io.h"
#include <EEPROM.h> // read and write from flash memory
#include <SPI.h>
#include <stdio.h>
#include <iostream>
#include <fstream>
// define the number of bytes you want to access
#define EEPROM_SIZE 1
// Pin definition for CAMERA_MODEL_AI_THINKER
#define PWDN_GPIO_NUM 32
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 0
#define SIOD_GPIO_NUM 26
#define SIOC_GPIO_NUM 27
#define Y9_GPIO_NUM 35
#define Y8_GPIO_NUM 34
#define Y7_GPIO_NUM 39
#define Y6_GPIO_NUM 36
#define Y5_GPIO_NUM 21
#define Y4_GPIO_NUM 19
#define Y3_GPIO_NUM 18
#define Y2_GPIO_NUM 5
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22
int pictureNumber = 0;
int CopyNumber = 0;
void imgWriter(const char *imgName,
unsigned char *buf,
int len,
int width,
int height)// WRITE A FILE
{
fs::FS &fs = SD_MMC;
Serial.printf("Picture file name: %s\n",imgName);
Serial.printf("%d\n",len);
Serial.printf("%d\n",width);
Serial.printf("%d\n",height);
File fo =fs.open(imgName,FILE_WRITE);
if(!fo){
Serial.println("Failed to open file in writing mode");
}
else {
fo.write(buf,len); // payload (image), payload length
Serial.printf("Saved file to path: %s\n" , imgName);
EEPROM.write(0, pictureNumber);
EEPROM.commit();
}
fo.close();
}
void copyImage(const char *imgName,
int width,
int height,
const char *copyimage) // COPY A FILE
{
fs::FS &fs = SD_MMC; // Allows access to SD card
File fo =fs.open(imgName,FILE_READ);
File fy =fs.open(copyimage,FILE_WRITE);
if(!fo){
Serial.printf("Unable to open file for reading");
}
unsigned char header[54];
unsigned char colorTable[1024];
for(int i =0 ; i<54 ; i++){
header[i]= getc(fo); // ERROR IS HERE
}
int width1 = * ( int * )&header[18]; //Width information of the image
int height1 = * ( int * ) &header[22];
int bitDepth = * ( int * ) &header[28];
if(bitDepth <= 8 ){
fo.read(colorTable,1024);
}
fy.write(header,54);
unsigned char buf[(height1*width1)];
fo.read(buf,(height1 * width1));
if(bitDepth <= 8){
fy.write(colorTable,1024);
}
fy.write(buf,(height1*width1));
fy.close();
fo.close();
Serial.printf("SUCCESS");
EEPROM.commit();
}
void setup() {
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
Serial.begin(115200);
//Serial.setDebugOutput(true);
//Serial.println();
camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = Y2_GPIO_NUM;
config.pin_d1 = Y3_GPIO_NUM;
config.pin_d2 = Y4_GPIO_NUM;
config.pin_d3 = Y5_GPIO_NUM;
config.pin_d4 = Y6_GPIO_NUM;
config.pin_d5 = Y7_GPIO_NUM;
config.pin_d6 = Y8_GPIO_NUM;
config.pin_d7 = Y9_GPIO_NUM;
config.pin_xclk = XCLK_GPIO_NUM;
config.pin_pclk = PCLK_GPIO_NUM;
config.pin_vsync = VSYNC_GPIO_NUM;
config.pin_href = HREF_GPIO_NUM;
config.pin_sscb_sda = SIOD_GPIO_NUM;
config.pin_sscb_scl = SIOC_GPIO_NUM;
config.pin_pwdn = PWDN_GPIO_NUM;
config.pin_reset = RESET_GPIO_NUM;
config.xclk_freq_hz = 20000000;
config.pixel_format = PIXFORMAT_JPEG;
if(psramFound()){
config.frame_size = FRAMESIZE_UXGA; // FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA
config.jpeg_quality = 10;
config.fb_count = 2;
} else {
config.frame_size = FRAMESIZE_SVGA;
config.jpeg_quality = 12;
config.fb_count = 1;
}
// Init Camera
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x", err);
return;
}
//Serial.println("Starting SD Card");
if(!SD_MMC.begin()){
Serial.println("SD Card Mount Failed");
return;
}
uint8_t cardType = SD_MMC.cardType();
if(cardType == CARD_NONE){
Serial.println("No SD Card attached");
return;
}
camera_fb_t * fb = NULL;
// Take Picture with Camera
fb = esp_camera_fb_get();
if(!fb) {
Serial.println("Camera capture failed");
return;
}
// initialize EEPROM with predefined size
EEPROM.begin(EEPROM_SIZE);
pictureNumber = EEPROM.read(0) + 1;
CopyNumber = EEPROM.read(0) + 1;
// Path where new picture will be saved in SD Card
String path = "/picture" + String(pictureNumber) +".bmp"; // Dosyanın ismi
String copy_path = "/Copy" + String(CopyNumber) +".bmp";
imgWriter(path.c_str(),fb->buf,fb->len,fb->width,fb->height); // WRITE FUNCION CALL
copyImage(path.c_str(),fb->width,fb->height,copy_path.c_str()); // Copy
esp_camera_fb_return(fb);
// Turns off the ESP32-CAM white on-board LED (flash) connected to GPIO 4
pinMode(4, OUTPUT);
digitalWrite(4, LOW);
rtc_gpio_hold_en(GPIO_NUM_4);
delay(2000);
Serial.println("Going to sleep now");
delay(2000);
esp_deep_sleep_start();
Serial.println("This will never be printed");
}
void loop() {
}
Error is : cannot convert 'fs::File' to 'FILE* {aka __sFILE*}' for argument '1' to 'int getc(FILE*)'
Contrary to the comments, your error is quite clear from the message and code:
You are trying to use a C library function getc with a ESP32 library object File.
That is impossible. getc can not work with this object, or any C++ object because it is a C function.
To read from the file object, you need to use its method read like this:
header[i] = fo.read();
You should read the library documentation for more information on how to use the File object, or see this tutorial on working with SD card:
https://diyi0t.com/sd-card-arduino-esp8266-esp32/

Delay within a timer (what is the correct approach)

I'm in a pickle regarding concepts relating to timers. How can I can I operate a "delay" inside a timer? This is the best way I can frame the question knowing full well what I'm trying to do is nonsense. The objective is: I wish to test the pinState condition 2 times (once initially and then 4 seconds later) but this all needs to happen periodically (hence a timer).
The platform is NodeMCU running a WiFi (ESP8266 chip) and coding done inside Arduino IDE.
#define BLYNK_PRINT Serial
#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>
BlynkTimer timer;
char auth[] = "x"; //Auth code sent via Email
char ssid[] = "x"; //Wifi name
char pass[] = "x"; //Wifi Password
int flag=0;
void notifyOnFire()
{
int pinState = digitalRead(D1);
if (pinState==0 && flag==0) {
delay(4000);
int pinStateAgain = digitalRead(D1);
if (pinStateAgain==0) {
Serial.println("Alarm has gone off");
Blynk.notify("House Alarm!!!");
flag=1;
}
}
else if (pinState==1)
{
flag=0;
}
}
void setup()
{
Serial.begin(9600);
Blynk.begin(auth, ssid, pass);
pinMode(D1,INPUT_PULLUP);
timer.setInterval(1000L,notifyOnFire);
}
void loop()
{
//Serial.println(WiFi.localIP());
Blynk.run();
timer.run();
}
an easy fix would be to set the periodicity of the timer to be 4000L timer.setInterval(4000L,notifyOnFire); and in notifyOnFire use a static variable and toggle its value whenever notifyOnFire is called
void notifyOnFire()
{
static char state = 0;
if( state == 0)
{
/* Write here the code you need to be executed before the 4 sec delay */
state = 1;
}
else
{
/* Write here the code you need to be executed after the 4 sec delay */
state = 0;
}
}
The nice thing about static variables is that they are initialized only once at compile time and they retain their values after the scope of code changes (In this case function notifyOnFire exits).

Getting volume value from pulseaudio

I've written this code by looking at various examples: Python pulseaudio monitor, Pavumeter source, async playback example, and Pacat source.
I have successfully connected to a sink and am able to record it, but my problem is, I'm stuck at getting the volume value out. If I try printing value from the read function, I just get a bunch of random numbers at a second's interval.
Now I'm not asking for someone to finish writing the code for me, I'd just like some tips, help so that I could head towards the right direction. How do I retrieve the volume value?
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <pulse/pulseaudio.h>
static int latency = 20000; // start latency in micro seconds
static int sampleoffs = 0;
static short sampledata[300000];
static pa_buffer_attr bufattr;
static int underflows = 0;
static pa_sample_spec ss;
// This callback gets called when our context changes state. We really only
// care about when it's ready or if it has failed
void pa_state_cb(pa_context *c, void *userdata) {
pa_context_state_t state;
int *pa_ready = userdata;
state = pa_context_get_state(c);
switch (state) {
// These are just here for reference
case PA_CONTEXT_UNCONNECTED:
case PA_CONTEXT_CONNECTING:
case PA_CONTEXT_AUTHORIZING:
case PA_CONTEXT_SETTING_NAME:
default:
break;
case PA_CONTEXT_FAILED:
case PA_CONTEXT_TERMINATED:
*pa_ready = 2;
break;
case PA_CONTEXT_READY:
*pa_ready = 1;
break;
}
}
static void stream_read_cb(pa_stream *s, size_t length, void *userdata) {
const void *data;
pa_stream_peek(s, &data, &length);
data = (const unsigned char*) data;
printf("%u", data);
pa_stream_drop(s);
}
int main(int argc, char *argv[]) {
pa_mainloop *pa_ml;
pa_mainloop_api *pa_mlapi;
pa_context *pa_ctx;
pa_stream *recordstream;
int r;
int pa_ready = 0;
int retval = 0;
unsigned int a;
double amp;
int test = 0;
// Create a mainloop API and connection to the default server
pa_ml = pa_mainloop_new();
pa_mlapi = pa_mainloop_get_api(pa_ml);
pa_ctx = pa_context_new(pa_mlapi, "Simple PA test application");
pa_context_connect(pa_ctx, NULL, 0, NULL);
// This function defines a callback so the server will tell us it's state.
// Our callback will wait for the state to be ready. The callback will
// modify the variable to 1 so we know when we have a connection and it's
// ready.
// If there's an error, the callback will set pa_ready to 2
pa_context_set_state_callback(pa_ctx, pa_state_cb, &pa_ready);
// We can't do anything until PA is ready, so just iterate the mainloop
// and continue
while (pa_ready == 0) {
pa_mainloop_iterate(pa_ml, 1, NULL);
}
if (pa_ready == 2) {
retval = -1;
goto exit;
}
ss.rate = 44100;
ss.channels = 2;
ss.format = PA_SAMPLE_U8;
recordstream = pa_stream_new(pa_ctx, "Record", &ss, NULL);
if (!recordstream) {
printf("pa_stream_new failed\n");
}
pa_stream_set_read_callback(recordstream, stream_read_cb, NULL);
r = pa_stream_connect_record(recordstream, NULL, NULL, PA_STREAM_PEAK_DETECT);
if (r < 0) {
printf("pa_stream_connect_playback failed\n");
retval = -1;
goto exit;
}
// Run the mainloop until pa_mainloop_quit() is called
// (this example never calls it, so the mainloop runs forever).
// printf("%s", "Running Loop");
pa_mainloop_run(pa_ml, NULL);
exit:
// clean up and disconnect
pa_context_disconnect(pa_ctx);
pa_context_unref(pa_ctx);
pa_mainloop_free(pa_ml);
return retval;
}
Looking at the original question from UNIX.StackExchange, it looks like you're trying to create a VU meter. It can be done using an envelope detector. You have to read the input values and then average their rectified value. A simple envelope detector can be done as an exponential moving average filter.
float level = 0; // Init time
const float alpha = COEFFICIENT; // See below
...
// Inside sample loop
float input_signal = fabsf(get_current_sample());
level = level + alpha * (input_signal - level);
Here, alpha is the filter coefficient, which can be calculated as:
const float alpha = 1.0 - expf( (-2.0 * M_PI) / (TC * SAMPLE_RATE) );
Where TC is known as the "time constant" parameter, measured in seconds, which defines how fast you want to "follow" the signal. Setting it too short makes the VU meter very "bumpy" and setting it too long will miss transients in the signal. 10 mS is a good value to start from.

Sending String from Processing to Arduino Not Working

I've collected some string information in processing and I am trying to send it to Arduino. I am getting processing to send the information, however, my output in arduino is weird. I get numbers like "77789...". I am not sure What I am doing wrong. All I need to do is, basically get a string from processing and send it to arduino to display it on a LCD screen.
Any help on this would be appreciated.
Here is my processing code:
import processing.serial.*;
Serial myPort;
XML MSFTxml; // loading Query
XML MSFT; // results of the query
XML row; // first row in the query
XML symbol; // Name of the stock
String symbolTxt;
String val;
void setup() {
size(200,200);
myPort = new Serial(this,"COM3", 9600);
}
void draw() {
updateXMLCall();
delay(10000);
}
void updateXMLCall () {
MSFTxml = loadXML("https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20csv%20where%20url%3D%27http%3A%2F%2Fdownload.finance.yahoo.com%2Fd%2Fquotes.csv%3Fs%3DMSFT%26f%3Dsl1d1t1c1ohgv%26e%3D.csv%27%20and%20columns%3D%27symbol%2Cprice%2Cdate%2Ctime%2Cchange%2Ccol1%2Chigh%2Clow%2Ccol2%27&format=xml&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys");
MSFT = MSFTxml.getChild("results"); //Getting the first tag "results" in the query MSFT
row= MSFT.getChild("row"); //first child tag "row"
symbol = row.getChild("symbol"); //name of the stock
symbolTxt = symbol.getContent().toString(); //converting the name of the stock into a string
myPort.write(symbolTxt);
println(symbolTxt);
}
Here is my arduino Code:
#include <Wire.h>
#include "rgb_lcd.h"
rgb_lcd lcd;
const int colorR = 50;
const int colorG = 0;
const int colorB = 0;
void setup()
{
Serial.begin(9600);
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
lcd.setRGB(colorR, colorG, colorB);
// Print a message to the LCD.
lcd.setCursor(0, 1);
}
void loop()
{
String content = "";
if (Serial.available()) {
while (Serial.available()) {
content += Serial.read();
lcd.print(content);
Serial.print(content);
lcd.setCursor(0, 1);
}
}
delay(100);
}
The problem is the use of += in C += doesn't concat strings.
You need to concatenate the char get from Serial.read() like here for example:
Convert serial.read() into a useable string using Arduino?

Resources