ESP32 BLE - how to declare a characteristic value that is read only and ESP_GATT_RSP_BY_APP? - bluetooth-lowenergy

Following the declaration of the IDX_CHAR_VAL_A value attribute in the ESPIDF example here https://github.com/espressif/esp-idf/blob/master/examples/bluetooth/bluedroid/ble/gatt_server_service_table/main/gatts_table_creat_demo.c#L189
If I change that declaration to allow read only and to use ESP_GATT_RSP_BY_APP instead of ESP_GATT_AUTO_RSP, is it ok to have the last three fields set to 0? My thinking is that the value buffer is not used anymore by the automatic value handling of the bluedroid stack, but couldn't find any mentioning or example in the ESP32 documentation.
That is, changing
[IDX_CHAR_VAL_A] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&GATTS_CHAR_UUID_TEST_A,
ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
GATTS_DEMO_CHAR_VAL_LEN_MAX, sizeof(char_value), (uint8_t *)char_value}},
to
[IDX_CHAR_VAL_A] =
{{ESP_GATT_RSP_BY_APP}, {ESP_UUID_LEN_16, (uint8_t *)&GATTS_CHAR_UUID_TEST_A,
ESP_GATT_PERM_READ,
0, 0, NULL}},

Related

STM32WB50CG - Correct way to update value of characteristic

I have a question about how to correctly update a value of characteristic of custom BLE service on STM32WB50CG. The project was set up with CubeMX following their official tutorial: https://www.youtube.com/watch?v=i10X4Blr8ns.
My main application is running on M4 core. It uses multiple sensors and then does various statistics with data taken from them. BLE stack is
running on M0+ core. I can see all the services & characteristics I defined in the app (BLE Toolbox from STM) on my Android phone. Now I am trying to figure out how to update characteristics from main
application. I tried to call those functions bellow with predefined value in my main app, but read value of characteristic on my Android phone is always 0.
Result status of aci_gatt_update_char_value is "success" when Custom_STM_App_Update_Char is called.
Functions I tried to call in my main application on M4 core to update characteristics (with fixed value of 5 for testing purposes)
Custom_STM_App_Update_Char(CUSTOM_STM_CVOLTAGE, (uint8_t*)5);
Custom_STM_App_Update_Char(CUSTOM_STM_CCURRENT, (uint8_t*)5);
Example of two update value functions for my characteristics
case CUSTOM_STM_CVOLTAGE:
result = aci_gatt_update_char_value(CustomContext.CustomVsHdle,
CustomContext.CustomCvoltageHdle,
0, /* charValOffset */
SizeCvoltage, /* charValueLen */
(uint8_t *) pPayload);
/* USER CODE BEGIN CUSTOM_STM_Service_1_Char_1*/
/* USER CODE END CUSTOM_STM_Service_1_Char_1*/
break;
case CUSTOM_STM_CCURRENT:
result = aci_gatt_update_char_value(CustomContext.CustomCsHdle,
CustomContext.CustomCcurrentHdle,
0, /* charValOffset */
SizeCcurrent, /* charValueLen */
(uint8_t *) pPayload);
/* USER CODE BEGIN CUSTOM_STM_Service_3_Char_1*/
/* USER CODE END CUSTOM_STM_Service_3_Char_1*/
break;
The first and most obvious issue with your code is that you are casting integer literal to the uint8_t address.
Put the value in a variable on the stack and pass the address of that variable to Custom_STM_App_Update_Char.
Instead of:
Custom_STM_App_Update_Char(CUSTOM_STM_CVOLTAGE, (uint8_t*)5);
do
uint8_t my_value = 5;
Custom_STM_App_Update_Char(CUSTOM_STM_CVOLTAGE, &my_value);

create array of ubo and present each at a time to its shader

i want to create array of ubo object in my cpu update it and then upload it to the gpu in one call like that. (for the example lets say i have only two objects).
std::vector<UniformBufferObject> ubo(m_allObject.size());
int index = 0;
for (RenderableObject* rendObj : m_allObject)
{
ubo[index].proj = m_camera->getProjection();
ubo[index].view = m_camera->getView();
ubo[index].model = rendObj->getTransform().getModel();
ubo[index].proj[1][1] *= -1;
index++;
}
int size = sizeof(UniformBufferObject) *m_allObject.size();
void* data;
m_instance->getLogicalDevice().mapMemory(ykEngine::Buffer::m_uniformBuffer.m_bufferMemory, 0, size , vk::MemoryMapFlags(), &data);
memcpy(data, ubo.data(), size);
m_instance->getLogicalDevice().unmapMemory(ykEngine::Buffer::m_uniformBuffer.m_bufferMemory);
i created one buffer with the size of two ubo. (the create do work because it do work with ubo in size one).
vk::DeviceSize bufferSize = sizeof(UniformBufferObject) * 2;
createBuffer(logicalDevice, bufferSize, vk::BufferUsageFlagBits::eUniformBuffer, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent, m_uniformBuffer.m_buffer, m_uniformBuffer.m_bufferMemory);
and than i put an offset in the descriptor set creation :
vk::DescriptorBufferInfo bufferInfo;
bufferInfo.buffer = uniformBuffer;
bufferInfo.offset = offsetForUBO;
bufferInfo.range = sizeof(UniformBufferObject);
the offset is the size of UniformBufferObject * the index of the object.
every object have is own descriptorsetLayout but the samepipline
when i try to update the descriptor set i get the error :
i couldnt find any aligment enum that specify that information.
if anyone know how to do that it will help alot.
thanks.
i couldnt find any aligment enum that specify that information.
Vulkan is not OpenGL; you don't use enums to query limits. Limits are defined by the VkPhysicalDeviceLimits struct, queried via vkGetPhysicalDeviceProperties/2KHR.
The error tells you exactly which limitation you violated: minUniformBufferOffsetAlignment. Your implementation set this to 0x100, but your provided offset was insufficient for this.
Also, you should not map buffers in the middle of a frame. All mapping in Vulkan is "persistent"; map it once and leave it that way until you're ready to delete the memory.

In Arduino, how do you write to port when port is a variable?

Examples of writing to a port seem to always use the port number as a constant, eg,
OCR2A = 180;
How do you write to the port when the port is unknown until run time. For example,
int port = (buttonPressed) ? 0x3b : 0x3c;
portWrite( port, 180 );
What I cannot find is the funtion portWrite(). Does something like that exist?
Robert's answer has some imprecise assertions and an incomplete answer.
Writing directly to port registers you can ruin other settings of the port and sometimes cause permanent damage to controller.
Can ruin other settings: true, you have to know what you are doing (for instance what pins are on the port you are manipulating, and know what are the functions you want to keep.
Can cause permanent damage: not really, or better not because of the port manipulation. If you wire a short circuit to ground and then set it as an output to 1, you can damage it whether you are using the port register or the digitalwrite. You have to be careful in both ways.
Now, returning to your problem, the enumeration is one way, but since the PORTB, PORTC, PORTD are just short name for values, you can set a variable and then use it to indirectly access it.
The type for this kind of variable is a volatile pointer to a byte (volatile means that write and read operations cannot be optimized by the compiler, since the value can change even between two operations):
volatile uint8_t *variablePortRegister;
You just have to load it with the address (so with the & symbol) of the register you want to change:
variablePortRegister = &PORTC;
then use the pointer to change the value
PORTC = 0x12;
becomes
(*variablePortRegister) = 0x12;
This is a short example. For it to work, connect a LED with resistor on arduino pin 5 (bit 5 of PORTD). The LED on the board (labeled L) is connected to pin 13 (bit 5 of PORTB).
The sketch will make one of the two leds blink for five times, then switch to the other. Only port manipulation instructions are used, and you can find that the way to read the port is the same as the one to write it.
volatile uint8_t *myportreg;
unsigned long lastTime;
uint8_t counter;
void setup() {
DDRB |= 0x20;
DDRD |= 0x20;
PORTB = 0;
PORTD = 0;
counter = 99; // trigger the register change immediately
}
void loop() {
if (counter >= 10)
{
counter = 0;
if (myportreg == &PORTD)
myportreg = &PORTB;
else
myportreg = &PORTD;
}
if ((millis() - lastTime) > 500)
{
lastTime = millis();
// change bit 5 of register
*myportreg = 0x20 ^ (*myportreg);
counter++;
}
}
EDIT: as Robert pointed out, it's much better to "use" just the pins you need (in this case, bit 5 of port B and D) rather than setting the whole port; this way you minimize the risk of screwing up something else. This edit is already included in the above code, so the code is correct
The port is a bit in one particular register. If you know the register and the position of the port in that particular register you can try this:
register = (1<<port) || register
to set the port to 1 and
register = (1<<port)^-1 && register
to set the port to 0.
Of course, you will need a switch somewhere to determine the register and the bit of the port in the register given the port name.

BLE GATT server data format

I'm playing on this example:
https://doc-snapshots.qt.io/qt5-dev/qtbluetooth-heartrate-server-example.html
to better understand how to configure a GATT server.
The example fakes a HeartRate profile. In detail it creates a characteristic with this client descriptor:
const QLowEnergyDescriptorData clientConfig(QBluetoothUuid::ClientCharacteristicConfiguration, QByteArray(2, 0));
from here:
https://developer.bluetooth.org/gatt/descriptors/Pages/DescriptorViewer.aspx?u=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
I understand it has both notifications and indications disabled by default (in fact I need to enable them from a client application in order to be notified).
What I really don't understand is this code:
quint8 currentHeartRate = 60;
const auto heartbeatProvider = [&service, &currentHeartRate, &valueChange]() {
QByteArray value;
value.append(char(0)); // Flags that specify the format of the value.
value.append(char(currentHeartRate)); // Actual value.
QLowEnergyCharacteristic characteristic = service->characteristic(QBluetoothUuid::HeartRateMeasurement);
service->writeCharacteristic(characteristic, value); // Potentially causes notification.
...
Well, it appends two bytes to the characteristic's value because it was defined above:
QLowEnergyCharacteristicData charData;
charData.setUuid(QBluetoothUuid::HeartRateMeasurement);
charData.setValue(QByteArray(2, 0));
but what does the first one mean?
value.append(char(0)); // Flags that specify the format of the value.
I cannot find any documentation about this "format".
The first byte is the flags field specified in the Heart Rate Service (HRS)here. In this example, flags field indicates that the heart rate measurement value is in uint8 format.

Onewire temperatures to MQTT broker server

I am trying to modify the code from the example included in the onewire library for arduino so that no matter how many onewire devices I have plugged it will always find them and publish it to a MQTT using the device ID and the current temperature. I have gotten it to publish the temperature, but am having trouble adding the device ID or ROM which is in HEX to my topic.
So for example i want it to appear like this. Note the topic and msg for MQTT need to be Char* (more info here: http://knolleary.net/arduino-client-for-mqtt/api/#publish1)
topic = Celsius eg 12.09
payload (or msg) = \home[ROM]\temperature\current eg. \home\2894AA6220025\temperature\current
(just an example of the output you normally get when you run the code without my additions, this is the serial output!! notice the ROM and celsius that I want to use)
Have put my full code here, it is just a modification of the included onewire example with the pubsub MQTT part added on.
(see line 155 onwards) https://gist.github.com/matbor/5931466
//publish the temp now to mqtt topic
String strTopic = "/house/285A9282300F1/temperature/current"; // need to replace the 285A9282300F1 with the ROM ID on each LOOP!
char charMsg[10];
String strMsg = dtostrf(celsius, 4, 2, charMsg); //convert celsius to char
char charTopic[strTopic.length() + 1];
//char charMsg[strMsg.length() + 1];
strTopic.toCharArray(charTopic, sizeof(charTopic));
strMsg.toCharArray(charMsg, sizeof(charMsg));
client.publish(charTopic,charMsg);
Add this to the top of your sketch, outside of the loop function:
char hexChars[] = "0123456789ABCDEF";
#define HEX_MSB(v) hexChars[(v & 0xf0) >> 4]
#define HEX_LSB(v) hexChars[v & 0x0f]
This defines a pair of macros that return the most-significant and least-significant bytes of an int as the appropriate HEX character. (There may be more appropriate built-in's for this, but this is what I use out of habit).
The following code will insert the ROM, as a HEX string, into the topic. Note you can create the topic as a char[] directly - you don't need to go via a String object.
char charTopic[] = "/house/XXXXXXXXXXXXXXXX/temperature/current";
for (i = 0; i < 8; i++) {
charTopic[7+i*2] = HEX_MSB(addr[i]);
charTopic[8+i*2] = HEX_LSB(addr[i]);
}
For the payload, I'm not sure if it is 100% necessary, but I always explicitly initialise any char[] to all 0's when using as a buffer. This ensures whatever is written into the buffer will definitely be null-terminated. Again, you don't need to go via String types:
char charMsg[10];
memset(charMsg,'\0',10);
dtostrf(celsius, 4, 2, charMsg);
Finally, publish the message:
client.publish(charTopic,charMsg);

Resources