dkettner / SmartForestMesh

A system to monitor deer sightings to make educated guesses about deer populations in a forest. This gets accomplished by connecting ESP32-CAMs via mesh and using a combination of presence detection and trained tensorflow models to make reports. These reports get sent to a destination node which may be connected to the internet.
7 stars 0 forks source link

taskSendReport: Failed to send report #1

Open chriswant opened 2 years ago

chriswant commented 2 years ago

Hello and Thank You for Your Time and Effort. I am trying to use your "SmartForestMesh" and I get this Error "taskSendReport: Failed to send report 349_249.log!". Currently, I am using 2x ESP32-CAM's with SD Cards in both. I use the Arduino IDE and copied the Main.cpp to a new .ino file and uploaded it to the ESP32-CAM. I am not using the PIR Motion sensor at the moment. I figured that every NODE on the mesh network would be able to receive and broadcast to each other and show the Logs coming from other NODE's in the Serial Monitor. But I don't think that this program works that way. A question to you is how can I set up a receiver NODE, or is there a way to use the Painlessmesh Android APP to display the Log Reports and Pictures when it connects with the mesh by collecting all NODE's SD card data or something like that. But the main question is how to set up a Receiver NODE to receive the Report Logs and display it via the serial monitor. Thank You again for your time and effort.

dkettner commented 2 years ago

@chriswant

Hi, sorry for the delayed response.

First of all this project is not finished (for example the PIR sensor and the tensorflow stuff is not working yet) and I am actually working on some other projects right now so there will be no fixes during the next weeks.

But I will try to answer your questions.

I figured that every NODE on the mesh network would be able to receive and broadcast to each other

A question to you is how can I set up a receiver NODE

It is indeed possible to either broadcast to every node or to send messages to specific nodes. As of right now the project sends the message to the node which is specified at the top (so no broadcast). So find out the ID of the receiving node and insert it there (DEST_NODE). You may change this destination ID for each node before uploading. If a node sends a message to itself, it will fail. If a node sends a message to another node which is not currently connected to the mesh, it will also fail. If you want to broadcast to every node you will have to change the class PictureReportPackage. It inherits the SinglePackage from the painlessmesh-library. You will most likely have to inherit some other class but you should look up the painlessmesh wiki for that. Also in setup() you will have to change the mesh.onPackage()-call. Depending on your usecase you may even use some simpler methods of the mesh and skip the defition of a new class.

and show the Logs coming from other NODE's in the Serial Monitor. But I don't think that this program works that way.

The logs are only meant to be displayed locally via the serial monitor of each node. They are not transmitted via mesh at all. There are two reasons for that. Firstly, the logs should give you hints if everything is working as expected. If the mesh is not working, the log will not reach other nodes, so it would be useless (in my case). Secondly, this would cause a huge overhead of messages floating around the mesh and I wanted to avoid that. Generally I would suggest that you only send messages that you actually need because the mesh (and the esp32s sending/receiving all kinds of messages) might overload and behave weird or take very long for a simple message to reach its destination.

But the main question is how to set up a Receiver NODE to receive the Report Logs and display it via the serial monitor.

I guess I kind of already answered this but I wanted to make clear what my code is actually supposed to do. It is supposed to take a picture, evaluate it (ideally with tensorflow but thats not working right now), build a report about the evaluation and send this report to the receiving node. This report is not what you see in the serial prints (which are just some logs with information about what is going on). So if you want to reuse the code for your project you should probably try to modify the report which is being send to include the information that you need instead of making every serial log a broadcast.

I hope this makes sense to you but feel free to ask again if I missed your point.

dkettner commented 2 years ago

is there a way to use the Painlessmesh Android APP to display the Log Reports and Pictures when it connects with the mesh by collecting all NODE's SD card data or something like that.

I never used the app, sorry.

I can tell you something about the pictures though: I did not manage to actually send pictures via mesh to another node (which is why the picuture is supposed to be evaluated on the esp32 and not on some remote server). This is because pictures are just too big. I read somewhere in the painlessmesh-repo that it should be possible to base64-encode the picture, send it via mesh and decode it on the receiving node. However, it simply did not work and I tried a lot. Please do come back to me if you succeed with this.

chriswant commented 2 years ago

@dkettner Hello and Thank You for your update on this matter. Yes, you have answered all my questions. I really like your coding style, I wish to be as good as you one day... (Baby-Steps). I am working on a project that uses the Painlessmesh Mesh Network for Sensors data collecting and displaying Graphs or Log txt of that data on a modified Painlessmesh Android APP. I am trying myself to Send the Pictures base64-encode through Json. With no solution yet but when I solve this I will let you know. Other than that your code has helped me a lot in Understanding, Structuring, and setting everything up to work together on these ESP32-CAM boards. Thank You again for your time and effort.

chriswant commented 2 years ago

@dkettner Hello, I think I have found a solution for sending images through JSON.
Here is the Link : https://gitlab.com/painlessMesh/painlessMesh/-/issues/426

This is where I found out how to send large data through JSON Packets. He seemed to have some trouble with it though. I made some modifications and got it to work.

Images are at VGA size with 18 Jpeg_quality. Makes a file about 8600 kb file size. It gets broken up into Chucks of 3000 kb and sent out by mesh.sendBroadcast();.

I think we could send larger size files but I think we need to figure out how to space out sending the data. (instead of all at once by a forloop). Maybe by saving all the chunks then putting them in a queue to be sent out over time, or something like that.

Here are the relevant parts to the code. Please if you can see what I have done to the code and see if there is something you would do differently, I would love to know how you would clean this up to work better.
Thank You for your time and effort.

To take the Picture:

`void sendData( String sensorType, long idPacket, int indexPacket, int totalPackets, String dataSensor ) { String msg;

DynamicJsonDocument jsonBuffer( 1024 + dataSensor.length() ); JsonObject root = jsonBuffer.to< JsonObject >(); root[ "nodeId" ] = nodeID; root[ "type" ] = "sensor"; JsonObject packet = root.createNestedObject( "packet" ); packet[ "sensor" ] = "sensorType"; packet[ "idPacket" ] = String( idPacket ); packet[ "indexPacket" ] = String( indexPacket ); packet[ "totalPackets" ] = String( totalPackets ); packet[ "data" ] = dataSensor; serializeJson( root, msg );

Serial.println( msg ); Serial.println( "---> Send " + sensorType + " packet " + String( indexPacket ) + "/" + String( totalPackets ) + " to " ); mesh.sendBroadcast( msg ); }

String encodedString;

define CFG_MESH_MAX_PACKET_SIZE 3000 //------------------------ This Same to work the best

void takePicture(); Task taskTakePicture(TASK_SECOND 30, TASK_FOREVER, &takePicture); // -------- For testing set to 30 sec void takePicture() { // Retrieve camera framebuffer camera_fb_t fb = NULL; uint8_t* _jpg_buf = NULL; size_t _jpg_buf_len = 0; esp_err_t res = ESP_OK; Serial.print( "Capturing Image .." );

fb = esp_camera_fb_get();

if ( !fb ) { Serial.println( "Camera capture failed" ); res = ESP_FAIL; } else { Serial.println( "Done!" );

  _jpg_buf_len = fb->len;
  _jpg_buf = fb->buf;

int encodedLength = _jpg_buf_len;
Serial.println( "Size of the base64 encoded image..." + String( encodedLength ) );
const String& encodedString = base64::encode((uint8_t *)_jpg_buf,  _jpg_buf_len);
Serial.println( "Encoded string created" );

int tailPacketLength = encodedLength % CFG_MESH_MAX_PACKET_SIZE;
long idPacket = random( LONG_MIN, LONG_MAX );

if ( tailPacketLength == encodedLength )
{
  sendData( "camera", idPacket, 1, 1, String( encodedString ) );
}
else
{
  String photoString( encodedString );
  int numPackets = ( encodedLength / CFG_MESH_MAX_PACKET_SIZE ) + 1;
  String fractPacket;
  int i;

  for ( i = 0; i < ( numPackets ); i++ )  // -----   "numPackets - 1"  THIS DID NOT WORK --------------------------------------   
  {
    fractPacket = photoString.substring( i * CFG_MESH_MAX_PACKET_SIZE, ( i * CFG_MESH_MAX_PACKET_SIZE ) + CFG_MESH_MAX_PACKET_SIZE );
    sendData( "camera", idPacket, i + 1, numPackets, String( fractPacket ) );
    vTaskDelay( 500 / portTICK_PERIOD_MS ); 
    fractPacket = "";
  }

  fractPacket = photoString.substring( i * CFG_MESH_MAX_PACKET_SIZE );
  sendData( "camera", idPacket, i + 1, numPackets, String( fractPacket ) );
  vTaskDelay( 500 / portTICK_PERIOD_MS );
  fractPacket = "";
}

if ( fb)
{
  esp_camera_fb_return( fb );
  fb = NULL;
  _jpg_buf = NULL;
}

}

if ( res != ESP_OK ) { return; } }

//Camera Settings

config.frame_size = FRAMESIZEVGA; // FRAMESIZE + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA config.jpeg_quality = 18; //10-63 lower number means higher quality config.fb_count = 1;

// Initialize the Camera esp_err_t err = esp_camera_init(&config); if (err != ESP_OK) { Serial.printf("Camera init failed with error 0x%x", err); return; }

sensor_t * s = esp_camera_sensor_get(); s->set_brightness(s, 0); // -2 to 2 s->set_contrast(s, 1); // -2 to 2 s->set_saturation(s, 0); // -2 to 2 s->set_special_effect(s, 0); // 0 to 6 (0 - No Effect, 1 - Negative, 2 - Grayscale, 3 - Red Tint, 4 - Green Tint, 5 - Blue Tint, 6 - Sepia) s->set_whitebal(s, 1); // 0 = disable , 1 = enable s->set_awb_gain(s, 1); // 0 = disable , 1 = enable s->set_wb_mode(s, 0); // 0 to 4 - if awb_gain enabled (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home) s->set_exposure_ctrl(s, 1); // 0 = disable , 1 = enable s->set_aec2(s, 0); // 0 = disable , 1 = enable s->set_ae_level(s, 0); // -2 to 2 s->set_aec_value(s, 300); // 0 to 1200 s->set_gain_ctrl(s, 1); // 0 = disable , 1 = enable s->set_agc_gain(s, 0); // 0 to 30 s->set_gainceiling(s, (gainceiling_t)0); // 0 to 6 s->set_bpc(s, 0); // 0 = disable , 1 = enable s->set_wpc(s, 1); // 0 = disable , 1 = enable s->set_raw_gma(s, 1); // 0 = disable , 1 = enable s->set_lenc(s, 1); // 0 = disable , 1 = enable s->set_hmirror(s, 0); // 0 = disable , 1 = enable s->set_vflip(s, 0); // 0 = disable , 1 = enable s->set_dcw(s, 1); // 0 = disable , 1 = enable s->set_colorbar(s, 0); // 0 = disable , 1 = enable `

To Receive the Picture:

`void receivedCallback( uint32_t from, String &msg ) { // Serial.printf("Received from %u msg=%s\n", from, msg.c_str());

DynamicJsonDocument jsonBufferFrom( 1024 + msg.length() ); DeserializationError error = deserializeJson( jsonBufferFrom, msg ); if ( error ) { Serial.print( F( "DeserializeJson() failed: " ) ); Serial.println( error.c_str() ); return; } JsonObject rootFrom = jsonBufferFrom.as< JsonObject >();

if ( rootFrom.containsKey( "type" ) && String( "sensor" ).equals( rootFrom[ "type" ].as< String >() ) ) { String msgTo;

  DynamicJsonDocument jsonBufferTo( 1024 + msg.length() );
  JsonObject rootTo = jsonBufferTo.to< JsonObject >();
  rootTo[ "nodeId" ] = rootFrom;
  rootTo[ "sensor" ] = rootFrom[ "packet" ][ "sensor" ].as< String >();
  rootTo[ "idPacket" ] = rootFrom[ "packet" ][ "idPacket" ].as< String >();
  rootTo[ "indexPacket" ] = rootFrom[ "packet" ][ "indexPacket" ].as< String >();
  rootTo[ "totalPackets" ] = rootFrom[ "packet" ][ "totalPackets" ].as< String >();
  rootTo[ "data" ] = rootFrom[ "packet" ][ "data" ].as< String >();
  serializeJson( rootTo, msgTo );

  Serial.println( msgTo );

}

}`

chriswant commented 2 years ago

Sorry, Still a NOOB at Posting. I hope you can still understand what I'm trying to get across.