How do I send any byte as a single ascii character?

OK. I see what you are getting at but we are all shooting in the dark here as we really don't know what @chrisgadg is doing and they refuse to post their sketch

The example in the first post is obviously not doing what the actual sketch does and this line of code in a later reply tells us nothing

pTxByte1Data->setValue(TX1ToSend); pTxByte1Data->notify();

apart from the fact that TX1ToSend is a string

Hopefully it is a string and not a String, but we don't even know that

PErhaps the OP does not realize that ASCII characters are only 7 bits wide, while a byte is 8 bits wide. An automatic limitation on what they want.

write() can send a byte array of whatever length you want, limited I would assume by the maximum size of an array. Just be careful when using a char array, that will be seen as a null terminated array so sending a zero value will be seen as the termination of the data.

I have been reading this topic but not responding, partly because others were providing excellent help and partly because I was not able to type a response.

It seems to me there is a misunderstanding underlying your question, although I am not sure if you are the one who is misunderstanding or me.

You want to send a single byte as... err, a single byte, yes? You have one byte and want to get it to the other end of a a serial connection as one byte, have I understood correctly? If so then send the byte exactly as it is, as one byte. What else do you think it can be sent as? Your single byte has 256 possible states, if you want to encode it into a single but different byte you still have only 256 possible states to play with, so there's no point, just send it. As others have pointed out Serial.write(single_byte) will do this for you.

Do please explain if I have misunderstood you.

sc < 256

Someone in the thread said "just send the value and reconstruct it at the other end however you want." I agree with that, because "printing ASCII" to the Serial Monitor is not representing all the bits sent. If you Serial.print(val, BIN) you will see none of the bits are lost, only represented as "bad character"... but all the bits are available for reconstruction as with a Protocol Analyzer (or Commodore64) character set for example CR, LF, SO, SI, SX, EX, et c.

(Commodore64 set here)

(Protocol Analyzer here... "low" ASCII values used for comms in RED)

OP's original sketch (for() statement corrected) with the BIN argument shows none of the bits are lost.

void setup() {
  Serial.begin(115200);
  for (int sc = 0; sc < 256; sc++) {
    Serial.print(sc);
    Serial.print(" ");
    // Serial.println((char)sc, BIN); // original line...
    Serial.println((byte)sc, BIN); // ... see post #28 
  }
}

void loop() {}

Results show all the bits... crazy what happens at DEC 128

0 0
1 1
2 10
3 11
4 100
5 101
6 110
7 111
8 1000
9 1001
10 1010
11 1011
12 1100
13 1101
14 1110
15 1111
16 10000
17 10001
18 10010
19 10011
20 10100
21 10101
22 10110
23 10111
24 11000
25 11001
26 11010
27 11011
28 11100
29 11101
30 11110
31 11111
32 100000
33 100001
34 100010
35 100011
36 100100
37 100101
38 100110
39 100111
40 101000
41 101001
42 101010
43 101011
44 101100
45 101101
46 101110
47 101111
48 110000
49 110001
50 110010
51 110011
52 110100
53 110101
54 110110
55 110111
56 111000
57 111001
52 110100
53 110101
54 110110
55 110111
56 111000
57 111001
58 111010
59 111011
60 111100
61 111101
62 111110
63 111111
64 1000000
65 1000001
66 1000010
67 1000011
68 1000100
69 1000101
70 1000110
71 1000111
72 1001000
73 1001001
74 1001010
75 1001011
76 1001100
77 1001101
78 1001110
79 1001111
80 1010000
81 1010001
82 1010010
83 1010011
84 1010100
85 1010101
86 1010110
87 1010111
88 1011000
89 1011001
90 1011010
91 1011011
92 1011100
93 1011101
94 1011110
95 1011111
96 1100000
97 1100001
98 1100010
99 1100011
100 1100100
101 1100101
102 1100110
103 1100111
104 1101000
105 1101001
106 1101010
107 1101011
108 1101100
109 1101101
110 1101110
111 1101111
112 1110000
113 1110001
114 1110010
115 1110011
116 1110100
117 1110101
118 1110110
119 1110111
120 1111000
121 1111001
122 1111010
123 1111011
124 1111100
125 1111101
126 1111110
127 1111111
128 11111111111111111111111110000000
129 11111111111111111111111110000001
130 11111111111111111111111110000010
131 11111111111111111111111110000011
132 11111111111111111111111110000100
133 11111111111111111111111110000101
134 11111111111111111111111110000110
135 11111111111111111111111110000111
136 11111111111111111111111110001000
137 11111111111111111111111110001001
138 11111111111111111111111110001010
139 11111111111111111111111110001011
140 11111111111111111111111110001100
141 11111111111111111111111110001101
142 11111111111111111111111110001110
143 11111111111111111111111110001111
144 11111111111111111111111110010000
145 11111111111111111111111110010001
46 11111111111111111111111110010010
147 11111111111111111111111110010011
148 11111111111111111111111110010100
149 11111111111111111111111110010101
150 11111111111111111111111110010110
151 11111111111111111111111110010111
152 11111111111111111111111110011000
153 11111111111111111111111110011001
154 11111111111111111111111110011010
155 11111111111111111111111110011011
156 11111111111111111111111110011100
157 11111111111111111111111110011101
158 11111111111111111111111110011110
159 11111111111111111111111110011111
160 11111111111111111111111110100000
161 11111111111111111111111110100001
162 11111111111111111111111110100010
163 11111111111111111111111110100011
164 11111111111111111111111110100100
165 11111111111111111111111110100101
166 11111111111111111111111110100110
167 11111111111111111111111110100111
168 11111111111111111111111110101000
169 11111111111111111111111110101001
170 11111111111111111111111110101010
171 11111111111111111111111110101011
172 11111111111111111111111110101100
173 11111111111111111111111110101101
174 11111111111111111111111110101110
175 11111111111111111111111110101111
176 11111111111111111111111110110000
177 11111111111111111111111110110001
178 11111111111111111111111110110010
179 11111111111111111111111110110011
180 11111111111111111111111110110100
181 11111111111111111111111110110101
182 11111111111111111111111110110110
183 11111111111111111111111110110111
184 11111111111111111111111110111000
185 11111111111111111111111110111001
186 11111111111111111111111110111010
187 11111111111111111111111110111011
188 11111111111111111111111110111100
189 11111111111111111111111110111101
190 11111111111111111111111110111110
191 11111111111111111111111110111111
192 11111111111111111111111111000000
193 11111111111111111111111111000001
194 11111111111111111111111111000010
195 11111111111111111111111111000011
196 11111111111111111111111111000100
197 11111111111111111111111111000101
198 11111111111111111111111111000110
199 11111111111111111111111111000111
200 11111111111111111111111111001000
201 11111111111111111111111111001001
196 11111111111111111111111111000100
197 11111111111111111111111111000101
198 11111111111111111111111111000110
199 11111111111111111111111111000111
200 11111111111111111111111111001000
201 11111111111111111111111111001001
202 11111111111111111111111111001010
203 11111111111111111111111111001011
204 11111111111111111111111111001100
205 11111111111111111111111111001101
206 11111111111111111111111111001110
207 11111111111111111111111111001111
208 11111111111111111111111111010000
209 11111111111111111111111111010001
210 11111111111111111111111111010010
211 11111111111111111111111111010011
212 11111111111111111111111111010100
213 11111111111111111111111111010101
214 11111111111111111111111111010110
215 11111111111111111111111111010111
216 11111111111111111111111111011000
217 11111111111111111111111111011001
218 11111111111111111111111111011010
219 11111111111111111111111111011011
220 11111111111111111111111111011100
221 11111111111111111111111111011101
222 11111111111111111111111111011110
223 11111111111111111111111111011111
224 11111111111111111111111111100000
225 11111111111111111111111111100001
226 11111111111111111111111111100010
227 11111111111111111111111111100011
228 11111111111111111111111111100100
229 11111111111111111111111111100101
230 11111111111111111111111111100110
231 11111111111111111111111111100111
232 11111111111111111111111111101000
233 11111111111111111111111111101001
234 11111111111111111111111111101010
235 11111111111111111111111111101011
236 11111111111111111111111111101100
237 11111111111111111111111111101101
238 11111111111111111111111111101110
239 11111111111111111111111111101111
240 11111111111111111111111111110000
241 11111111111111111111111111110001
242 11111111111111111111111111110010
243 11111111111111111111111111110011
244 11111111111111111111111111110100
245 11111111111111111111111111110101
246 11111111111111111111111111110110
247 11111111111111111111111111110111
248 11111111111111111111111111111000
249 11111111111111111111111111111001
250 11111111111111111111111111111010
251 11111111111111111111111111111011
252 11111111111111111111111111111100
253 11111111111111111111111111111101
254 11111111111111111111111111111110
255 11111111111111111111111111111111

Eastern hemisphere computers have been using "double-byte encoding" to represent their pictogram and syllabary... probably earlier than 2000 (my last work translating single-byte western encoding to double-byte eastern).

When your receiving end sees 64 > ASCII < 127, replace it with your own character.

==> Serial.println((byte)sc, BIN);

A lot of confusion arises because the serial Monitor often doesn’t show what you think you’ve sent , often ( always ?) showing the ascii character if you say “ write” a number and view on the serial monitor.

You can get free serial decoders that can be configured to show the format you want (bytes, ascii ,hex etc)

( showing off here) my Rigol scope can decode the serial data so you can see the nuts and bolts of the message by hooking on the transmit line . I’d imagine you could do the same with a cheap logic analyser , never tried it .

When I’ve done this type of thing I send bytes with serial write and at the other end read them into a byte array ( as per Perry ) You need to fix the message length or have a known value to detect the end of the sent data ( which won’t ever appear in the data stream!) . Works for me.

If you are unwilling to share your superb none working code , write something short to illustrate the issue and post that .

The other bit you're missing is that the Serial Monitor, like many things nowadays, actually supports UTF-8. So any sequence of bytes with the high bit set will attempt to be decoded as such.

  • if it is a properly encoded character
    • and the font actually has it, that's what you'll see
    • if not you'll see something else
  • if it's not a valid sequence, you'll get that "bad character" placeholder, often for every 2, 3, or maybe 4 bytes.

That does not mean that byte did not go "over the wire" exactly as you expected to -- it (likely) did. It's how those bytes are interpreted on the other end.

ETA: if you want to be able to send a sequence of bytes and see those as printable ASCII characters: that's what Base64 is for. Or you can cook up a similar scheme to keep the visible set the same -- e.g. 65 is just A -- and carve out some reserved set to encode 0-31 and 128-150. Might be fun.

The characters in post 10 are drawing characters and each requires 3 bytes so your example took 18 bytes to generate.

You can exceed 127 characters that will display in the Arduino serial monitor but there is going to be a cost in the number of bytes. If you are on Windows and take a look at code page 850 (Wikipedia) all but 4 of the characters are printable but about 124 of them require multiple bytes. @PerryBebbington said it as clear as it could be said in post 24, this gives 256 byte values that you can do what you want with. If it's the printable images that you want then you will need to decode from a code page.

An example of 2 & 3 byte code

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

}

void code_2_byte(byte b1){
  byte byte_1 = (b1 & 0xf0) >> 6 ;
  byte byte_2 = b1 & 0x3f;

  char c[3]={byte_1 | 0xc0, byte_2 | 0x80};
  Serial.println(c);

}

void code_3_byte(uint16_t b1){
  byte byte_1 = (b1 & 0xf000) >> 12 ;
  byte byte_2 = (b1 & 0xfc0) >> 6 ;
  byte byte_3 = (b1 & 0x3f) ;

  char c[4]={(byte_1 | 0xe0 ), (byte_2 | 0x80) , (byte_3 | 0x80)};
  Serial.println(c);

}

void loop() {

  code_2_byte(0xa3);
  code_3_byte(0x2663);

  delay(1000);

}

The issue has been resolved within the time constraints given.

I have several numbers to send over Bluetooth from 0 to 150 and originally the best seen was sending each as a single ascii character, where up to 20 could go in one send.

My first solution was to look for a setting which enabled 256 character set to be sent, or send as 7 bit plus the high bytes going in bitfields.

I'm able to send the maximum of five bytes at a time in a string of individual ascii characters, the sending takes about 50 microseconds and the next (different) packet the same time is needed. Solved. I do not have time to experiment further with it.

How many characters are being sent during this 50 us period and at what Bd?

I'm deeply confused after reading 30+ posts on a relatively simple question/problem about ASCII and bytes to be sent via serial. And after the author of the post said he wouldn't post any code (not even a working "derived" code example, just to explain and demonstrate what he wants to achieve), I think the discussion should have ended there.

In any case, since the OP seems to simply need to send bytes representing values ​​between 0 and 150 (but since he hasn't explained what they're for, it's hard to be sure this is actually his requirement), the answer in my opinion is simple and has already been partially explained: he shouldn't use Serial.print() but Serial.write() to send the bytes, i.e., the individual values ​​between 0 and 150 (or at least up to 255).

Without more information on the project and its purpose, I don't think there are any other possible answers (and the thread would have stopped at a handful of posts, not 33!).

1.
ASCII is 7-bit long (Fig-1)
byte is 8-bit long


Figure-1:

2.
So, when we execute: Serial.print('A'), I think, a zero is appened at b7-position to make it 8-bit what we call "character length".

3. In serial communication, the frame structure is (Fig-2):


Figure-2:

Frame length    : 10-bit without parity bit
Character length: 8-bit
ASCII code      : 7-bit
Start bit       : 1
Stop bit        : 1

I know this very well (I've been on IT since 1985...), but the person who started the thread seems to only need to send integer values ​​between 0 and (at most) 150, i.e., a byte, so the ASCII encoding table is completely irrelevant.

In my opinion, this whole pointless discussion stems from a misunderstanding on the person who started the thread, because he/she asked "how do I send a byte as a single ASCII character?" so the term "ASCII" makes no sense if this requirement is to send "byte" values, and not "ASCII" characters.

I hope I've clarified my point better (perhaps also to avoid further clogging up this thread with irrelevant comments and posts).

So it seems to me that you finally still not understand the difference between character and byte and, if (when) you experienced the similar problem again, you again will not know how to go with it

For anyone not quite sure on the difference, it may help to note

char c;
Serial.print(c);

Go to Definition on print:

size_t Print::print(char c)
{
  return write(c);
}

passes the argument directly to write. Hovering over write, the bottom of the tooltip says

// In Print
public: virtual size_t write(uint8_t) = 0

Printing a character sends the byte over the wire -- no difference.

Printing a byte prints its numeric value with one or more digits.

uint8_t b;
Serial.print(b);

The fact that it takes an optional second argument for the base

// In Print
public: size_t print(unsigned char, int = DEC)

which defaults to decimal, is a clue.

And also note, three kinds of char

  • unsigned char: the smallest unsigned number range: 0 to 255
  • signed char: a number from -128 to 127
  • just char: same size, one byte, but intended for a character. Might actually be either signed or unsigned when converted to an int for example.

Extra credit

Looking at the overloads for write in Print.h

    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);
    }
  • send a byte: pure virtual, implemented by the subclass. This does all the work.
  • send a C-string: for nullptr, do nothing; but otherwise get its length, then cast it to treat it like a byte buffer
  • send a char buffer: cast it to treat it like a byte buffer
  • send a byte buffer: the implementation is at the top of Print.cpp; it just loops
/* default implementation: may be overridden */
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;
}

HardwareSerial muddies the situation by adding overloads to write that take wider "numbers". But all they do

    virtual size_t write(uint8_t);
    inline size_t write(unsigned long n) { return write((uint8_t)n); }
    inline size_t write(long n) { return write((uint8_t)n); }
    inline size_t write(unsigned int n) { return write((uint8_t)n); }
    inline size_t write(int n) { return write((uint8_t)n); }

is cast to truncate to byte. They don't take a second base argument.

So to send a byte, write is always safer. But you really can just print a char to do the same thing.

What to do on the receiving end....

What wold be the above definition if the char c is replaced by byte y?

None. If you call print() with another argument type, it is not the above definition that will be called because the function has another overload (if you know the meaning of "overloads" in OOP). In this case, "byte" is defined as an alias for "uint8_t", so it's just an integer and will be printed as a number. Example:

  char c = 'A';
  byte y = 65; // (ASCII code for 'A')
  
  Serial.print(c); // output: A
  Serial.print(y); // oputput: 65

Why not --

Serial.print(y); // oputput: 65
==> 
Serial.write(0x36);   //output: 6
Serial.write(0x35);  //output: 5