C/C++ Question Error while parsing B4R library header

max123

Well-Known Member
Licensed User
Longtime User
Hi all,

I started to convert my ESP8266 and Arduino libraries to B4R platform, but encountered some problems.

I used PSPad as text editor to edit my first library, I want suggest all to use PSPad because is a fantastic application and show highlight syntax for any language, custom syntax can be added, and have more tools inside it, eg, to convert colors, to preview an html page and others good tools. I never tried Notepad++ but I think is like PSPad.

I'm started to open on PSPad some B4R libraries, just to know how works, so I've open rEEPROM, rSD, rESP8266 etc...

I tried to convert header and cpp file of my first library, then I used my tool B4Rh2xml UI
https://www.b4x.com/android/forum/t...eate-b4r-xml-file-from-b4r-header-file.80940/
to create a xml file from header file, but always I receive a parse error, now I want to know what is the problem.

I've attached an image with error I receive from parser, note that I edited a cpp file too.
What is wrong on my header file?

This is original header file:
B4X:
#ifndef FCM_ESP8266_h
#define FCM_ESP8266_h

#include <ESP8266WiFi.h>

#define DEFAULT_TITLE F("This is default title")
#define DEFAULT_MESSAGE F("Hello from ESP8266!")

//#define FCM_DEBUG       // <<<<<<<<---------- UNCOMMENT THIS TO SHOW SERIAL DEBUG. NOTE: USE MORE SRAM
#define FIELD_COUNT  30   // <<<<<<<<---------- INCREASE THIS NUMBER TO USE CUSTOM FIELDS

class FCM_ESP8266
{
  private:
    String _API_key;
    WiFiClient client;

  public:
    FCM_ESP8266(String API_key);

    typedef struct{
      String key;
      String value;
    } keyValuePair;

    bool sendMessage(String tokens[], unsigned int tokensLen, String From, String Title, String Message);
    bool sendMessage(String tokens[], unsigned int tokensLen);
    void resetFields();
  
    keyValuePair field[FIELD_COUNT];
};

#endif
And this is my conversion for B4R (rESP8266FirebaseCloudMessaging.h):
B4X:
#pragma once

#include "B4RDefines.h"
//~dependson: <ESP8266WiFi.h>

#define DEFAULT_TITLE "This is default title"
#define DEFAULT_MESSAGE "Hello from ESP8266!"

//#define FCM_DEBUG       // <<<<<<<<---------- UNCOMMENT THIS TO SHOW SERIAL DEBUG. NOTE: USE MORE SRAM
#define FIELD_COUNT  30   // <<<<<<<<---------- INCREASE THIS NUMBER TO USE CUSTOM FIELDS

//~Version: 1.02
namespace B4R {
    //~shortname: FirebaseCloudMessaging
   class B4RFirebaseCloudMessaging
   {
     private:
       B4RString _API_key;
       WiFiClient client;

     public:
       B4RFirebaseCloudMessaging(B4RString* API_key);

       typedef struct{
         B4RString key;
         B4RString value;
       } keyValuePair;
  
       bool sendMessage(B4RString* tokens[], UInt tokensLen, B4RString* From, B4RString* Title, B4RString* Message);
       bool sendMessage(B4RString* tokens[], UInt tokensLen);
       void resetFields();
     
       keyValuePair field[FIELD_COUNT];
   };
}

Many thanks to all.
 

Attachments

  • ScreenShot.png
    ScreenShot.png
    106.6 KB · Views: 517
Last edited:

Erel

B4X founder
Staff member
Licensed User
Longtime User
1. Prefer to use arrays of bytes over strings.
2. There is no real reason to create a C library for this feature. It is better to write a B4R code module with this. You can use rHttpUtils2 to make the requests. It is very simple.
This way other developers can modify the code as needed.
3. The class constructor cannot accept any parameter.
4. I don't see how you will be able to store the strings data in your class (unless you plan to use malloc which is not recommended).
5. You will need to hide the field variable. It is not a B4R array so it cannot be accessed directly.
 

max123

Well-Known Member
Licensed User
Longtime User
Hi Erel,

1. Ok, I will try to change, I think this is like a char* array on C++? I avoided this because String class use more ram but helped me to finish my work (I'm not a very expert) tried this using char arrays with functions like strcmp, strlen, mempcy, memset and all <cstring> (string.h) set of commands but difficult for me. For reason that I've used String and not char arrays, my library works ok without impact on ESP8266 that has a lot of ram, just use a small 3%, on Arduino Mega works ok too, but on Arduino Uno a simple code fits all ram, so works but with some limitations. If now I use bytes arrays I can optimize to fits on Uno too.
2. Ok, I will try to write a code module, I downloaded rHttpUtils2 code module. I had thought this because I created an installer (attached image) that automatically installs the libraries (note that these are different for each platform, ESP8266, ArduinoWiFi and ArduinoEth), this installer opens and unpacks a .zip file that contains the interest library and automatically copies it to the <libraries> folder of Arduino, I wanted to add B4R to this and make sure that the library was copied into the Additional Libraries folder of B4R, but I think I can do the same with a code module. Yes with code module the users can change as want, so useful, thanks for this idea ;)
3. Ok, I will try to move this parameter API_KEY on the setApiKey method and leave constructor without parameters.
4. Sorry but I do not understand what you mean, actually I do not use malloc, just declared some Strings, so construct a request based on passed parameters, then do the request to Firebase service, this is a small part of code:
B4X:
// make http POST request
    String Header = "";
    Header += F("POST /fcm/send HTTP/1.1\n");
    Header += F("Host: "); Header += serverName; Header += "\n";
    Header += F("User-Agent: Arduino\n");
    Header += F("Content-Type: application/json\n");
    Header += F("Authorization: key="); Header += _API_key; Header += "\n";
    Header += F("Content-length: "); // has to be exactly the number of characters (bytes) in the POST Body
5. I need to use ~hide? Please which is the field variable in my B4R header file?

PS. I've already wrote same library for B4A, then B4J in UI mode and B4J NONUI mode that works on Desktop and on Raspberry PI too using command line or creating an automated script. These libraries will exit next days, I already have a full package with demo code for any platform, but not B4R. In the package is included a B4A app that work with a Service that start on boot and receive messages. This app support custom fields, allowed even vocal and persistent notifications. These libraries already use HttpUtils2 and HttpUtils2_NONUI, so I start from here....

Many thanks

https://drive.google.com/open?id=0B_2FkqqAjcsETkxlSm1lQ0ExWWM
 
Last edited:

Erel

B4X founder
Staff member
Licensed User
Longtime User
It is hard for me to explain the memory issues as you need to have a good understanding of Arduino memory system.

The code you posted is problematic as it will eventually cause memory fragmentation (it uses malloc internally).
However I wasn't talking about this. I was talking about the KeyValuePair objects that you are storing. It is not clear for me how you will be able to store the actual strings.
 

max123

Well-Known Member
Licensed User
Longtime User
Hi Erel,

following your suggest I've converted my C++ FirebaseCloudMessaging library to a B4R code module, initially I've worked using array of bytes, but as you says I've a big fragmentation, ESP8266 crashes, next I've tried using Strings, but same issue. So nothing works after a week of work on this... :(

In my code modules I've created a KeyValuePair object and declared 30 fields:

Using Bytes:
B4X:
    Type KeyValuePair(key() As Byte, val() As Byte)
Public field(30) As KeyValuePair      ' <<<<<<<<---------- INCREASE THIS NUMBER TO USE MORE CUSTOM FIELDS
Using Strings:
B4X:
    Type KeyValuePair(key As String, val As String)
Public field(30) As KeyValuePair      ' <<<<<<<<---------- INCREASE THIS NUMBER TO USE MORE CUSTOM FIELDS
Then I fill this way:
B4X:
    field(0).key = "fromme"
field(0).val = From
but after a bit I've missing data or strange results on fields I've previousely filled.
Tried to set a StackBufferSize to 2000, 3000, 4000, 5000 bytes, but this not solved, there are issues in the code.

I've copied/pasted a code directly from C++ library and then adapted to B4R, so the flow it is the same.

I attached both, one use Bytes and one use Strings, please, can you see what is wrong on my code modules?

Many thanks
 

Attachments

  • FirebaseCloudMessaging_BYTES.zip
    10.9 KB · Views: 452
  • FirebaseCloudMessaging_STRINGS.zip
    11.5 KB · Views: 459
Last edited:

max123

Well-Known Member
Licensed User
Longtime User
Yes Erel, I've tried this, but how to change programmatically the fields? I've used JoinBytes, but ESP8266 crashes on it.

This is the code:
B4X:
#Region Project Attributes
   #AutoFlushLogs: True
   #CheckArrayBounds: True
   #StackBufferSize: 1000
#End Region

Sub Process_Globals
    Public Serial1 As Serial
    Private wifi As ESP8266WiFi
    Private bc As ByteConverter
    Private raf As RandomAccessFile
    Private API_KEY() As Byte = "AIzaSy..........."
    Private const SSID As String =  "NETWORK-SSID"
    Private const PASS As String =  "NETWORK-PASS"
  
    Type KeyValuePair(key() As Byte, val() As Byte)
    Public field(30) As KeyValuePair      ' <<<<<<<<---------- INCREASE THIS NUMBER TO USE MORE CUSTOM FIELDS
End Sub

Private Sub AppStart
    Serial1.Initialize(115200)
    Log(" ") : Log(" ") : Log("AppStart") : Log(" ")
    Log("Connecting to ", SSID)
    '    If wifi.Connect(SSID) Then 'change as needed
    If Not(wifi.Connect2(SSID, PASS)) Then
        Log("Failed to connect")
        Return
    End If
    Log("Connected to Network")
  
    'send the android message
    SendMessage("general", "ESP8266", "This is the title", "This is the message")  'send Android message
End Sub

Sub JobDone (jr As JobResult)
    Log("JobDone: ", jr.JobName)
    Log("Success: ", jr.Success)
    If jr.JobName = "ios_general" Then  
        SendMessage("ios_general", "ESP8266", "This is the title", "This is the message")   'send the ios message (cambiare il nome del topic in  ios_general )
    End If
End Sub

Private Sub SendMessage(Topic() As Byte, From() As Byte, Title() As Byte, Message() As Byte)
    HttpJob.Initialize(bc.StringFromBytes(Topic))  
    Dim buffer(800) As Byte 'must be large enough to hold the message payload
    field(0).key = "from" : field(0).val = From
'    field(1).Key = "to" : field(1).val = tokens(0) 'RegID
    field(2).key = "title" : field(2).val = Title
    field(3).key = "message" : field(3).val = Message

    raf.Initialize(buffer, True)
    WriteBytes(raf, JoinBytes(Array("{""data"":{""", field(0).key, """:""")))
    WriteBytes(raf, field(0).val)
'    WriteBytes(raf, "{""data"":{""title"":""")
    WriteBytes(raf, JoinBytes(Array(""":{""", field(2).key, """:""")))
    WriteBytes(raf, field(2).val)
'    WriteBytes(raf, """,""body"":""")
    WriteBytes(raf, JoinBytes(Array(""",""", field(3).key, """:""")))
    WriteBytes(raf, field(3).val)
    WriteBytes(raf, """}")
    'end of data
    WriteBytes(raf, ",""to"":""\/topics\/")
    WriteBytes(raf, Topic)
    WriteBytes(raf, """")
    WriteBytes(raf, ",""priority"": 10")
  
    If bc.StartsWith(Topic, "ios_") Then
        WriteBytes(raf, ",""notification"": {""title"": """)
        WriteBytes(raf, field(2).val)
        WriteBytes(raf, """,""body"":""")
        WriteBytes(raf, field(3).val)
        WriteBytes(raf, """, ""sound"": ""default""}")
    End If
  
    WriteBytes(raf, "}")
  
    Log("REQUEST BODY:")
    Log(bc.StringFromBytes(raf.ReadBytes2(raf.CurrentPosition, 0)))
  
    HttpJob.AddHeader("Authorization", JoinBytes(Array("key=".GetBytes, API_KEY)))
    HttpJob.AddHeader("Content-Type", "application/json")
    Log("stack: ", StackBufferUsage, ", buffer size:", raf.CurrentPosition)
    HttpJob.Post("https://fcm.googleapis.com/fcm/send", bc.SubString2(buffer, 0, raf.CurrentPosition))
End Sub

Private Sub WriteBytes(r As RandomAccessFile, Data() As Byte)
    r.WriteBytes(Data, 0, Data.Length, raf.CurrentPosition)
End Sub
 
Top