Created
February 5, 2025 05:11
-
-
Save kelvinresch/31550dc33466a8ae7c41eedabb109b60 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "BLECStringCharacteristic.h" | |
#include "EString.h" | |
#include "RobotCommand.h" | |
#include <ArduinoBLE.h> | |
//////////// BLE UUIDs //////////// | |
#define BLE_UUID_TEST_SERVICE "4afe7319-bc9f-49b7-95d8-15246fd2aeee" | |
#define BLE_UUID_RX_STRING "9750f60b-9c9c-4158-b620-02ec9521cd99" | |
#define BLE_UUID_TX_FLOAT "27616294-3063-4ecc-b60b-3470ddef2938" | |
#define BLE_UUID_TX_STRING "f235a225-6735-4d73-94cb-ee5dfce9ba83" | |
//////////// BLE UUIDs //////////// | |
//////////// Global Variables //////////// | |
BLEService testService(BLE_UUID_TEST_SERVICE); | |
BLECStringCharacteristic rx_characteristic_string(BLE_UUID_RX_STRING, BLEWrite, MAX_MSG_SIZE); | |
BLEFloatCharacteristic tx_characteristic_float(BLE_UUID_TX_FLOAT, BLERead | BLENotify); | |
BLECStringCharacteristic tx_characteristic_string(BLE_UUID_TX_STRING, BLERead | BLENotify, MAX_MSG_SIZE); | |
// RX | |
RobotCommand robot_cmd(":|"); | |
// TX | |
EString tx_estring_value; | |
float tx_float_value = 0.0; | |
long interval = 500; | |
static long previousMillis = 0; | |
unsigned long currentMillis = 0; | |
//////////// Global Variables //////////// | |
int values[2000]; | |
float temps[2000]; | |
int values_pos = 0; | |
enum CommandTypes { | |
PING, | |
SEND_TWO_INTS, | |
SEND_THREE_FLOATS, | |
ECHO, | |
DANCE, | |
SET_VEL, | |
GET_TIME_MILLIS, | |
UPDATE_TIME_DATA, | |
SEND_TIME_DATA, | |
GET_TIME, | |
GET_TEMP_READINGS, | |
}; | |
void handle_command() { | |
// Set the command string from the characteristic value | |
robot_cmd.set_cmd_string(rx_characteristic_string.value(), | |
rx_characteristic_string.valueLength()); | |
bool success; | |
int cmd_type = -1; | |
// Get robot command type (an integer) | |
/* NOTE: THIS SHOULD ALWAYS BE CALLED BEFORE get_next_value() | |
* since it uses strtok internally (refer RobotCommand.h and | |
* https://www.cplusplus.com/reference/cstring/strtok/) | |
*/ | |
success = robot_cmd.get_command_type(cmd_type); | |
// Check if the last tokenization was successful and return if failed | |
if (!success) { | |
return; | |
} | |
// Handle the command type accordingly | |
switch (cmd_type) { | |
/* | |
* Write "PONG" on the GATT characteristic BLE_UUID_TX_STRING | |
*/ | |
case PING: | |
tx_estring_value.clear(); | |
tx_estring_value.append("PONG"); | |
tx_characteristic_string.writeValue(tx_estring_value.c_str()); | |
Serial.print("Sent back: "); | |
Serial.println(tx_estring_value.c_str()); | |
break; | |
/* | |
* Extract two integers from the command string | |
*/ | |
case SEND_TWO_INTS: | |
{ | |
int int_a, int_b; | |
// Extract the next value from the command string as an integer | |
success = robot_cmd.get_next_value(int_a); | |
if (!success) | |
return; | |
// Extract the next value from the command string as an integer | |
success = robot_cmd.get_next_value(int_b); | |
if (!success) | |
return; | |
Serial.print("Two Integers: "); | |
Serial.print(int_a); | |
Serial.print(", "); | |
Serial.println(int_b); | |
break; | |
} | |
/* | |
* Extract three floats from the command string | |
*/ | |
case SEND_THREE_FLOATS: | |
{ | |
/* | |
* Your code goes here. | |
*/ | |
float f_a, f_b, f_c; | |
// Extract the next value from the command string as an integer | |
success = robot_cmd.get_next_value(f_a); | |
if (!success) | |
return; | |
// Extract the next value from the command string as an integer | |
success = robot_cmd.get_next_value(f_b); | |
if (!success) | |
return; | |
// Extract the next value from the command string as an integer | |
success = robot_cmd.get_next_value(f_c); | |
if (!success) | |
return; | |
Serial.print("Three Floats: "); | |
Serial.print(f_a); | |
Serial.print(", "); | |
Serial.print(f_b); | |
Serial.print(", "); | |
Serial.println(f_c); | |
break; | |
} | |
/* | |
* Add a prefix and postfix to the string value extracted from the command string | |
*/ | |
case ECHO: | |
{ | |
char char_arr[MAX_MSG_SIZE]; | |
// Extract the next value from the command string as a character array | |
success = robot_cmd.get_next_value(char_arr); | |
if (!success) | |
return; | |
/* | |
* Your code goes here. | |
*/ | |
tx_estring_value.clear(); | |
tx_estring_value.append("Robot says -> "); | |
tx_estring_value.append(char_arr); | |
tx_characteristic_string.writeValue(tx_estring_value.c_str()); | |
Serial.print("Sent back: "); | |
Serial.println(char_arr); | |
break; | |
} | |
/* | |
* DANCE | |
*/ | |
case DANCE: | |
Serial.println("Look Ma, I'm Dancin'!"); | |
break; | |
/* | |
* SET_VEL | |
*/ | |
case SET_VEL: | |
break; | |
case GET_TIME_MILLIS: | |
{ | |
unsigned long time; | |
time = millis(); | |
tx_estring_value.clear(); | |
tx_estring_value.append("T:"); | |
tx_estring_value.append((double)time); | |
tx_characteristic_string.writeValue(tx_estring_value.c_str()); | |
Serial.print("Sent back: T:"); | |
Serial.println(time); | |
break; | |
} | |
case UPDATE_TIME_DATA: | |
{ | |
unsigned long t_0 = millis(); | |
while (values_pos != (sizeof(values) / sizeof(values[0]))) { | |
values[values_pos] = (int)(millis() - t_0); | |
values_pos++; | |
delay(10); | |
} | |
break; | |
} | |
case SEND_TIME_DATA: | |
{ | |
Serial.println("SEND_TIME_DATA start"); | |
int count = 0; | |
while (count < (sizeof(values) / sizeof(values[0]))) { | |
tx_estring_value.clear(); | |
while ((tx_estring_value.get_length() < 120) && (count < (sizeof(values) / sizeof(values[0])))) { | |
tx_estring_value.append(values[count]); | |
count++; | |
if ((tx_estring_value.get_length() < 119) && (count < (sizeof(values) / sizeof(values[0])))) { | |
tx_estring_value.append("|"); | |
} else { | |
break; | |
} | |
} | |
tx_characteristic_string.writeValue(tx_estring_value.c_str()); | |
delay(25); | |
} | |
Serial.println("SEND_TIME_DATA complete"); | |
break; | |
} | |
case GET_TIME: | |
{ | |
unsigned long t_0 = millis(); | |
int count = 1; | |
while (millis() - t_0 < 3000) { | |
tx_estring_value.clear(); | |
tx_estring_value.append((int)(millis() - t_0)); | |
tx_estring_value.append(","); | |
tx_estring_value.append(count); | |
tx_characteristic_string.writeValue(tx_estring_value.c_str()); | |
count++; | |
} | |
break; | |
} | |
case GET_TEMP_READINGS: | |
{ | |
Serial.println("Collecting Data..."); | |
unsigned long t_0 = millis(); | |
while (values_pos != (sizeof(values) / sizeof(values[0]))) { | |
values[values_pos] = (int)(millis() - t_0); | |
temps[values_pos] = getTempDegF(); | |
values_pos++; | |
delay(10); | |
} | |
values_pos = 0; | |
Serial.println("Finished collecting data. Sending now."); | |
Serial.println("SEND_TEMP_DATA start"); | |
int count = 0; | |
while (count < (sizeof(values) / sizeof(values[0]))) { | |
tx_estring_value.clear(); | |
while ((tx_estring_value.get_length() < 100) && (count < (sizeof(values) / sizeof(values[0])))) { | |
tx_estring_value.append(values[count]); | |
tx_estring_value.append(","); | |
tx_estring_value.append(temps[count]); | |
count++; | |
if ((tx_estring_value.get_length() < 99) && (count < (sizeof(values) / sizeof(values[0])))) { | |
tx_estring_value.append("|"); | |
} else { | |
break; | |
} | |
} | |
tx_characteristic_string.writeValue(tx_estring_value.c_str()); | |
delay(30); | |
} | |
Serial.println("SEND_TEMP_DATA complete"); | |
break; | |
} | |
/* | |
* The default case may not capture all types of invalid commands. | |
* It is safer to validate the command string on the central device (in python) | |
* before writing to the characteristic. | |
*/ | |
default: | |
Serial.print("Invalid Command Type: "); | |
Serial.println(cmd_type); | |
break; | |
} | |
} | |
void setup() { | |
values[0] = 1.0; | |
Serial.begin(115200); | |
BLE.begin(); | |
// Set advertised local name and service | |
BLE.setDeviceName("Artemis BLE"); | |
BLE.setLocalName("Artemis BLE"); | |
BLE.setAdvertisedService(testService); | |
// Add BLE characteristics | |
testService.addCharacteristic(tx_characteristic_float); | |
testService.addCharacteristic(tx_characteristic_string); | |
testService.addCharacteristic(rx_characteristic_string); | |
// Add BLE service | |
BLE.addService(testService); | |
// Initial values for characteristics | |
// Set initial values to prevent errors when reading for the first time on central devices | |
tx_characteristic_float.writeValue(0.0); | |
/* | |
* An example using the EString | |
*/ | |
// Clear the contents of the EString before using it | |
tx_estring_value.clear(); | |
// Append the string literal "[->" | |
tx_estring_value.append("[->"); | |
// Append the float value | |
tx_estring_value.append(9.0); | |
// Append the string literal "<-]" | |
tx_estring_value.append("<-]"); | |
// Write the value to the characteristic | |
tx_characteristic_string.writeValue(tx_estring_value.c_str()); | |
// Output MAC Address | |
Serial.print("Advertising BLE with MAC: "); | |
Serial.println(BLE.address()); | |
BLE.advertise(); | |
} | |
void write_data() { | |
currentMillis = millis(); | |
if (currentMillis - previousMillis > interval) { | |
tx_float_value = tx_float_value + 0.5; | |
tx_characteristic_float.writeValue(tx_float_value); | |
if (tx_float_value > 10000) { | |
tx_float_value = 0; | |
} | |
previousMillis = currentMillis; | |
} | |
} | |
void read_data() { | |
// Query if the characteristic value has been written by another BLE device | |
if (rx_characteristic_string.written()) { | |
handle_command(); | |
} | |
} | |
void loop() { | |
// Listen for connections | |
BLEDevice central = BLE.central(); | |
// If a central is connected to the peripheral | |
if (central) { | |
Serial.print("Connected to: "); | |
Serial.println(central.address()); | |
// While central is connected | |
while (central.connected()) { | |
// Send data | |
write_data(); | |
// Read data | |
read_data(); | |
} | |
Serial.println("Disconnected"); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment