Go Down

Topic: Why Serial.write() instead of Serial.print() (Read 2010 times) previous topic - next topic

J-M-L

#15
Jun 16, 2019, 08:49 am Last Edit: Jun 16, 2019, 10:21 am by J-M-L
@Golam

Quote
The source codes that you have presented explain the Mechanics and not the Mechanism of the working principles (at conceptual level) of the .print() and .write() methods.
I do have explained the fundamental mechanism - what the compiler does to find which function to call. It's signature dependent. I attached to source to show that the compiler dies not perform the base 10 conversion but that it is actually the library code which does the right work.

At method level description, print transforms its input in ASCII representation if needed (write("hello") is the same output as print("hello")) and write does not pre-process the input.

I feel You are totally free to explain things the way you want and i feel I'm totally free to call this BS when you make statements such as the compiler  transforms the number in base 10...
Quote
Now, the compiler transforms 0x41 into its decimal equivalent which is 65 (because of 10 base) and then executes the following two codes one after another. As a result, we see 65 on the Serial Monitor.
Code: [Select]
Serial.write(0x36);    //transmits ASCII code of 6; as a result, 6 appears on Serial Monitor
Serial.write(0x35);  //transmits ASCII code of 5; as a result, 5 appears on Serial Monitor
This is plain wrong. Note that I'm offering my opinion on the quality of the explanation and it's not a value judgement on your person. And BS is not as bad as "alternate truth" or "lie"...

Yes a write happens at the end because this is the way the Print class works with write being the only virtual function you need to implement to get printing working. But the bytes values that are sent are not black magic work done by the compiler but the result of carefully crafted algorithms written by a developer with clear intent

Explaining how the compiler picks a code to execute when multiple methods have the same name but different signature is in my opinion what the right answer to the question.

Now you are totally free to think differently and call also my explanation BS


PS/
Quote
Serial.write() which is executed by the MCU to place data byte into the actual transmitter of the UART Port and not this code: Serial.print(),
if you want to be technically accurate, you should state that the write method does  directly transfer the byte in the register ONLY if the serial buffer is empty (for efficiency reason) otherwise it's added to the end of the circular buffer (possibly after blocking if buffer is full) until an interrupt later will picks up the right byte and that's where t is actually being put in the register. That's why you don't need to worry about a risk of writing too fast in the register and can issue multiple write next too each other
Hello - Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums.
Bonjour Pas de messages privés SVP, postez dans le forum directement pour que ça profite à tous

Juraj

In that case, I would go on transferring 'Intel-Hex' formatted frame/file which is what you have mentioned in your above quote. In Intel-Hex format, usually the colon (: = 0x3A) is transmitted first as the beginning of a frame.
Intel HEX format is "human readable", not binary. I commented the unpractical binary protocol in your previous comment.

GolamMostafa

#17
Jun 16, 2019, 10:30 am Last Edit: Jun 16, 2019, 10:37 am by GolamMostafa
Note that I'm offering my opinion on the quality of the explanation and it's not a value judgement on your person. And BS is not as bad as "alternate truth" or "lie"...
I acknowledge and appreciate your sincere efforts so engaged for the elimination of misconceptions that I might have conceived owing to my very nature of self-learning through experiments and logical matching without really looking into the actual implementations of the developers.

J-M-L

I acknowledge and appreciate your sincere efforts so engaged for the elimination of misconceptions that I might have conceived owing to my very nature of self-learning through experiments and logical matching without really looking into the actual implementations of the developers.
I think the main misconception is failure to understand how the compiler picks the right method based on signature, hence leading to different code being executed.

Portraying this as magic done by the compiler transforming parameters automagically is very far from the technical truth and not leading learners towards the right path.

But again, that's just my view, your poetical interpretation of the compiler looking after you and understanding your intent is refreshing. May be when AI gets better ?
Hello - Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums.
Bonjour Pas de messages privés SVP, postez dans le forum directement pour que ça profite à tous

Juraj

#19
Jun 16, 2019, 12:19 pm Last Edit: Jun 16, 2019, 12:24 pm by Juraj
all print functions (same set of println exists) in Print.h

Code: [Select]
    size_t print(const __FlashStringHelper *);
    size_t print(const String &);
    size_t print(const char[]);
    size_t print(char);
    size_t print(unsigned char, int = DEC);
    size_t print(int, int = DEC);
    size_t print(unsigned int, int = DEC);
    size_t print(long, int = DEC);
    size_t print(unsigned long, int = DEC);
    size_t print(double, int = 2);
    size_t print(const Printable&);

* __FlashStringHelper is for the F() macro
* Printable& is for classes implementing Printable (only example is IPAddress)

and all write functions
Code: [Select]
    virtual size_t write(uint8_t) = 0;
    size_t write(const char *str) {
      if (str == NULL) return 0;
      return write((const uint8_t *)str, strlen(str));
    }
    virtual size_t write(const uint8_t *buffer, size_t size);
    size_t write(const char *buffer, size_t size) {
      return write((const uint8_t *)buffer, size);
   }

pure virtual  write(uint8_t) must be implemented in the derived class

and to be complete, implementation of write(buffer, size) in Print.cpp:
Code: [Select]
size_t Print::write(const uint8_t *buffer, size_t size)
{
  size_t n = 0;
  while (size--) {
    if (write(*buffer++)) n++;
    else break;
  }
  return n;
}


so simple

J-M-L

Hello - Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums.
Bonjour Pas de messages privés SVP, postez dans le forum directement pour que ça profite à tous

TEAC

Practice the following sketches to see the mechanism of extracting decimal number at the receiver side after collecting the data bytes transferred by the sender using .write() methods.

UNO-1 (Sender Codes)
Code: [Select]
#include<SoftwareSerial.h>
SoftwareSerial SUART(2, 3); //SRX/STX pin of UNO

void setup()
{
  Serial.begin(9600);
  SUART.begin(9600);
}

void loop()
{
  SUART.write('<'); //Start Mark of Message
  SUART.write(0x31);    //sends 1
  SUART.write(0x32);    //sends 2
  SUART.write(0x33);    //sends 3
  SUART.write(0x34);    //send 4
  SUART.write('>'); //End Mark of Message
  delay(1000);

}


UNO-2 (Receiver Codes)
Code: [Select]
#include<SoftwareSerial.h>
SoftwareSerial SUART(2, 3); //SRX/STX pin of NANO
byte myData[10];
bool flag1 = false;
int i = 0;
int x1, x2, x;

void setup()
{
  Serial.begin(9600);
  SUART.begin(9600);
}

void loop()
{
  byte n = SUART.available(); //checking if a data byte has arrived
  {
    if (n != 0)
    {
      if (flag1 == false)   //Start Mark has not arrived
      {
        byte x = SUART.read();
        if (x == '<')
        {
          flag1 = true;   //Start Mark has arrived
        }
      }
      else    //Start Mark has already arrived
      {
        byte y = SUART.read();
       // Serial.println(y, HEX);
        if (y != '>')
        {
          myData[i] = y;
          i++;
        }
        else
        {
          x = extractDecimal();    //extract decimal number for myData array
          Serial.println(x, HEX);    //shows received decimal number: 1234
          flag1 = false;
          i = 0; //reset array
        }
      }
    }
  }
}

int extractDecimal()
{
  x1 = (int)((myData[0] - 0x30)<< 4)| (int)(myData[1] - 0x30); //12
  x2 = (int)((myData[2] - 0x30) << 4) | (int)(myData[3] - 0x30);  //34
  x = (x1 << 8) | x2;
  return x;
}


BTW:  Give a try to re-write the receiver codes using the following method:
Code: [Select]
SUART.readBytesUntil('>', myArray, 10); //what would be the type of myArray -- byte or char?

Hey I have tried it and it didn't work however I have some questions. The SUART(2,3) means I have to connect the TX from the BT module to digital 2 and RX to digital 3 from the arduino uno? Also, should I be able to read the information from one Serial monitor to another? Eg: I send info from Arduino 1 (COM8) by writing on the Serial monitor and read it on Arduino 2 (COM9) in the serial monitor?

J-M-L

are you sure your BT module supports 5V for Rx ?
Hello - Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums.
Bonjour Pas de messages privés SVP, postez dans le forum directement pour que ça profite à tous

GolamMostafa

#23
Jun 16, 2019, 07:20 pm Last Edit: Jun 16, 2019, 07:21 pm by GolamMostafa
Hey I have tried it and it didn't work however I have some questions. The SUART(2,3) means I have to connect the TX from the BT module to digital 2 and RX to digital 3 from the arduino uno? Also, should I be able to read the information from one Serial monitor to another? Eg: I send info from Arduino 1 (COM8) by writing on the Serial monitor and read it on Arduino 2 (COM9) in the serial monitor?
The posted sketches have been tested on two UNOs being connected solidly via SUART (Software UART) Ports where the connections are to be made as follows:
DPin-3 of UNO-1 (STX) ----> DPin-2 of UNO-2 (SRX)
DPin-2 of UNO-1 (STX) <---- DPin-3 of UNO-2 (STX)
GND of UNO-1 <------------> GND of UNO-2  

If you are using BT, the connections would be as follows:

Go Up