Mohamed Saleh Mohamed Saleh - 4 months ago 11
C++ Question

Can't understand this line of code C++

I wish someone explain to me the following

I have C++ code which includes this line during initialization

ESPCommand<BufferedSerial> esp(D1, D0, PTC6);


where ESPCommand is a class, and BufferedSerial is a class, it's my first time to see this type of initialization what does it mean? here's the classes

class BufferedSerial : public RawSerial
{
private:
MyBuffer <char> _rxbuf;
MyBuffer <char> _txbuf;
uint32_t _buf_size;
uint32_t _tx_multiple;

void rxIrq(void);
void txIrq(void);
void prime(void);

public:
/** Create a BufferedSerial port, connected to the specified transmit and receive pins
* @param tx Transmit pin
* @param rx Receive pin
* @param buf_size printf() buffer size
* @param tx_multiple amount of max printf() present in the internal ring buffer at one time
* @param name optional name
* @note Either tx or rx may be specified as NC if unused
*/
BufferedSerial(PinName tx, PinName rx, uint32_t buf_size = 256, uint32_t tx_multiple = 4,const char* name=NULL);

/** Destroy a BufferedSerial port
*/
virtual ~BufferedSerial(void);

/** Check on how many bytes are in the rx buffer
* @return 1 if something exists, 0 otherwise
*/
virtual int readable(void);

/** Check to see if the tx buffer has room
* @return 1 always has room and can overwrite previous content if too small / slow
*/
virtual int writeable(void);

/** Get a single byte from the BufferedSerial Port.
* Should check readable() before calling this.
* @return A byte that came in on the Serial Port
*/
virtual int getc(void);

/** Write a single byte to the BufferedSerial Port.
* @param c The byte to write to the Serial Port
* @return The byte that was written to the Serial Port Buffer
*/
virtual int putc(int c);

/** Write a string to the BufferedSerial Port. Must be NULL terminated
* @param s The string to write to the Serial Port
* @return The number of bytes written to the Serial Port Buffer
*/
virtual int puts(const char *s);

/** Write a formatted string to the BufferedSerial Port.
* @param format The string + format specifiers to write to the Serial Port
* @return The number of bytes written to the Serial Port Buffer
*/
virtual int printf(const char* format, ...);

/** Write data to the Buffered Serial Port
* @param s A pointer to data to send
* @param length The amount of data being pointed to
* @return The number of bytes written to the Serial Port Buffer
*/
virtual ssize_t write(const void *s, std::size_t length);
};


and this is the other one

template <typename SERIAL>
class ESPCommand {
private:
SLIPPacket<SERIAL> _slip;
DigitalIn _button;

public:
ESPCommand(PinName tx, PinName rx, PinName button) :
_slip(tx, rx), _button(button){}

private:
bool command_start(char cmd, uint16_t len) {
len -= 4; // First word not included in length

return _slip.req_start() &&
_slip.putc(0x00) &&
_slip.putc(cmd) &&
_slip.send(&len, 2);
}

bool command_flush() {
uint16_t len;

return _slip.resp_start() &&
_slip.getc() == 0x01 &&
_slip.getc() >= 0 &&
_slip.recv(&len, 2) &&
_slip.recv(0, 4+len-2) &&
_slip.getc() == 0x00 &&
_slip.getc() >= 0 &&
_slip.resp_finish();
}

bool command_finish() {
return _slip.req_finish() &&
command_flush();
}

public:
bool sync() {
#ifdef LED_STATUS
led_green = 0; // Show progress
#endif
while (true) {
if (sync_frame()) {
#ifdef LED_STATUS
led_green = 1; // Show progress
#endif
return true;
}
}
}

bool sync_frame() {

// Flush serial line
_slip.flush();

printf("\r\nPower cycle ESP, put into FW Update mode, push user button\r\n");
int temp_button = _button;
while( temp_button == _button){;} // wait for button press
printf("\r\nContinuing Now\r\n");

// Send sync frame
uint32_t x = 0;

if (!(command_start(ESP_SYNC_FRAME, 2*4 + 32) &&
_slip.send(&x, 4) &&
_slip.putc(0x07) &&
_slip.putc(0x07) &&
_slip.putc(0x12) &&
_slip.putc(0x20)))
return false;

for (int i = 0; i < 32; i++) {
if (!_slip.putc(0x55))
return false;
}

if (!command_finish())
return false;

for (int i = 0; i < 7; i++) {
if (!command_flush())
return false;
}

return true;
}

bool flash_start(uint32_t blocks, uint32_t block_size, uint32_t address) {
uint32_t x = 0;
uint32_t size = blocks * block_size;

return command_start(ESP_FLASH_START, 5*4) &&
_slip.send(&x, 4) &&
_slip.send(&size, 4) &&
_slip.send(&blocks, 4) &&
_slip.send(&block_size, 4) &&
_slip.send(&address, 4) &&
command_finish();
}

bool flash_data(const char *data, uint32_t len, uint32_t n) {
uint32_t zero = 0;

uint32_t x = 0xef;
for (int i = 0; i < len; i++) {
x ^= data[i];
}

return command_start(ESP_FLASH_DATA, 5*4 + len) &&
_slip.send(&x, 4) &&
_slip.send(&len, 4) &&
_slip.send(&n, 4) &&
_slip.send(&zero, 4) &&
_slip.send(&zero, 4) &&
_slip.send(data, len) &&
command_finish();
}

bool flash_finish() {
uint32_t x = 0;

return command_start(ESP_FLASH_FINISH, 2*4) &&
_slip.send(&x, 4) &&
_slip.send(&x, 4) &&
command_finish();
}

bool flash_write(uint32_t address, const char *data, uint32_t size) {
uint32_t blocks = (size-1)/FLASH_BLOCK_SIZE + 1;

printf("Synching...\r\n");
if (!sync())
error("Sync error!");

printf("Starting transfer 0x%05x - 0x%05x\r\n", address, address + blocks*FLASH_BLOCK_SIZE);
if (!flash_start(blocks, FLASH_BLOCK_SIZE, address))
return false;

for (int i = 0; i < blocks; i++) {
printf("Flashing address 0x%05x\r\n", address + i*FLASH_BLOCK_SIZE);
if (!flash_data(&data[i*FLASH_BLOCK_SIZE], FLASH_BLOCK_SIZE, i))
return false;
}

printf("Finishing transfer\r\n");
if (!flash_finish())
return false;

wait_ms(250);
return true;
}
};

Answer

ESPCommand is a template class. In ESPCommand<BufferedSerial>, BufferedSerial is a template parameter. Templates helps having generic types/classes which can be reused with different template arguments.

For instance, you may need to write an array class storing ints, then an array class storing float. To avoid writing twice the same code with different types, you can use templates to write the code once which will be valid for any given type. This is what std::vector or std::list does. There are a lot of template classes already defined in the C++ standard library.

In you case, ESPCommand seems to be a class which known commands to be sent to something (is this a modem ?), and BufferedSerial is the type which knows how to communicate with the device. Assembling both using templates generates a type which is able to send the command over a buffered serial line (I guess). You may change the template argument to something else if the communication layer is different.