Hi,
Thanks for the awesome hardware and software!
After some weeks of struggling and posting on discord, Erik very wisely told me to post the Issue here.
The idea is I wait for the user to connect to esp32 and when so, sends a start message to the oak that enables a boolean so it calculates all the things I want the camera to do. (Oak receiving and sending messages inside a script) When a user disconnects it sends a message to the oak to stop and so it does not do any kind of calculations that eat CPU ram and battery if a user is not there.
I am doing by the moment with a boolean but I'd love to be able to actually 'stop' the pipeline or calculations.
Erik suggested this:
cfg = dai.CameraControl()
cfg.setStopStreaming()
ctrlQ.send(cfg)
which I have seen it makes the camera disconnect but not 'stop'.
Now that I have put it in context I will explain the issue/s here.
First of all, I had some issues of communication between the OAK D-Lite and the esp-32 that I 'fixed' by switching to a 'blocking'
call to getData() and try installing different versions of the software till now that I am using 2.15.0.0.
data = node.io['spimetain'].get().getData() # this is blocking
Because of this, at the end of the while loop of the esp32, I do send a message that is always the same 'ack' (acknowledgment. TCP/IP vibes 😎) so I do 'keep' the camera alive.
What happens to me now is the part of the oak receiving the 'start' action on the script while true works the boolean is set to True and starts sending the calculations from the Camera out till after 2 to 3 seconds after, it stops sending data out and itself blocks and so esp32 also shows timeout messages.
I have reduced any possible errors on my code by looking at documentation and code samples and I tested script node communication example adding a while(1) so it works on loop and the oak does not clog up.
Following the same philosophy I found out in my case it does clog up and I am sending messages of a size of 32 bytes for example and a maximum size of 1500 bytes. But I have seen that by only sending 32 bytes it does also clog up so it is not a message size thing.
Here is how I create the nodes:
print("Creating SPI in node...")
spiIn = pipeline.create(dai.node.SPIIn)
spiIn.setStreamName("spimetain")
spiIn.setBusId(0)
spiIn.out.link(script.inputs['spimetain'])
# set up SPI out node and link to nn1
spiOut = pipeline.create(dai.node.SPIOut)
spiOut.setStreamName("spimetaout")
spiOut.setBusId(0)
spiOut.input.setBlocking(False)
spiOut.input.setQueueSize(2000)
print("Creating SPI out node...")
script.outputs['host'].link(spiOut.input)
script.outputs['camControl'].link(cam.inputControl)
imu = pipeline.create(dai.node.IMU)
imu.enableIMUSensor([dai.IMUSensor.ARVR_STABILIZED_GAME_ROTATION_VECTOR], 500)
imu.setBatchReportThreshold(1)
imu.setMaxBatchReports(10)
spiOut1 = pipeline.create(dai.node.SPIOut)
spiOut1.setStreamName("spiimuout")
spiOut1.setBusId(0)
spiOut1.input.setBlocking(False)
spiOut1.input.setQueueSize(2)
# Link plugins IMU -> XLINK
imu.out.link(spiOut1.input)
Here is the while true inside the script that reads incoming messages and sends out a response accordingly:
while True:
data = node.io['spimetain'].get().getData() #this is blocking
jsonStr = str(data, 'utf-8')
msg_dict = json.loads(jsonStr)
${_TRACE1} (f"Manager received: {msg_dict}")
if msg_dict['action'] == 'stop':
${_TRACE1} ("Stop")
result = dict([("action", 'stopped')])
send_result(result)
cfg_cam_control.setStopStreaming()
node.io['camControl'].send(cfg_cam_control)
calc = False
elif msg_dict['action'] == 'start':
${_TRACE1} ("Start")
result = dict([("action", 'started')])
send_result(result)
cfg_cam_control.setStartStreaming()
node.io['camControl'].send(cfg_cam_control)
calc = True
elif msg_dict['action'] == 'syn':
${_TRACE1} ("MCU is alive")
result = dict([("action", 'ack')])
send_result(result)
elif msg_dict['action'] == 'ack':
${_TRACE1} ("MCU is alive")
if calc:
// calculates some data and sends to host that sends to spi
Here is the reduced esp32 code that tries receiving a message from oak and sends an Acknowledgement or ack message.
uint8_t req_success = 0;
mySpiApi.set_send_spi_impl(&esp32_send_spi);
mySpiApi.set_recv_spi_impl(&esp32_recv_spi);
delay(1000);
while(1) {
client_available = check_if_client_is_available();
if (receivedStartMessage == false && client_available)
{
ESP_LOGI(TAG, "Client available and no start message received. Means we have a new user connected or is resuming a previous session");
start_camera();
}
/* STEP 2: If client is available we send message to camera to start */
if (client_available) {
ESP_LOGI(TAG, "Client available INSIDE IF");
if (mySpiApi.req_message(&received_msg, SPI_IMU_OUT)) {
if (received_msg.raw_meta.size > 0)
{
ESP_LOGI(TAG, "Received metadata from camera (Probably IMU data): %s\n", received_msg.raw_meta.data);
// dai::RawIMUData det;
// mySpiApi.parse_metadata(&received_msg.raw_meta, det);
// for(const auto& det : det.packets){
// printf("x: %f, y: %f, z: %f, w:%f \n", det.rotationVector.i, det.rotationVector.j, det.rotationVector.k, det.rotationVector.real);
// }
}
}
if(mySpiApi.req_message(&received_msg , METASTREAM))
{
if (received_msg.raw_data.size > 0)
{
ESP_LOGI(TAG, "Received message from camera: %s\n", received_msg.raw_data.data);
// parse json message
cJSON *root;
root = cJSON_ParseWithLength((char *)received_msg.raw_data.data, received_msg.raw_data.size);
cJSON *action;
action = cJSON_GetObjectItemCaseSensitive(root, "action");
if (action != NULL) {
// parse oak response from our start or stop action
parse_action(action);
}
else
{
if (receivedStartMessage)
{
// we have received the calculations from the oak
ESP_LOGI(TAG,"Received message on loop: %s\n", received_msg.raw_data.data);
exampleDecodeRawMobilenet(received_msg.raw_data.data, received_msg.raw_data.size);
}
}
mySpiApi.free_message(&received_msg);
req_success = mySpiApi.spi_pop_messages();
ESP_LOGI(TAG, "FREEING MESSAGE req_success: %d\n", req_success);
}
else{
ESP_LOGI(TAG, "Received empty message from camera \n");
}
}
else
{
String error = "Camera not available yet\n";
if (receivedStartMessage)
{
error = "Camera timeout or something\n";
}
on_timeout(error);
}
}
send_message_to_oak("action", "ack");
if (receivedStartMessage == false && client_available == false){
// ESP_LOGI(TAG, "KEEPING LOOP BUSY");
delay(5000);
}else{
// delay(10);
sleep(0.1);
}
}
Thanks for the support
Ask me whatever you need and if I should modify this issue or give more context.