Mods to HardwareSerial to handle 9-bit data

Uno with the 328P SMD R2

I guess the version I published for Teensy 3.1, based on Nick's code, but with changes to use Arduino newer Serial.begin(baud, format)... isn't going to help you much.

I'm getting an error what says it cannot find Serial.write9bit. Not sure why, I've installed the 9bit library properly.

Mike

I've installed the 9bit library properly.

Obviously, you haven't.

Hi @ all,

i want use the HardwareSerial document´s from Nick(thank´s Nick for the help) but i must send a framing and overrun error flag with my 16-bit short.

I think it´s the easyest way to set it at bit position 10 and 11, but i´m new at arduino and didn´t know where i must write it in the code. It will be nice when someone can help me and explain where can i change it or what must i do.

with frindly wishes sniffi

Thanks, Nick Gammon for sharing the library, can you please tell library is capable of receiving 9 bits data. As I want to read 9bit commands and have to create its response accordingly. If activating 9 bit on Arduino how will I monitor or send the bits as the computer only supports 8 bits of serial communication?

Thanking you in anticipation.

This code has been reworked to compile under version 1.6.9 of the IDE. The earlier version for 1.0.1 is available from http://gammon.com.au/Arduino/HardwareSerial9bit_IDE_1.0.1.zip

The newer version is at http://gammon.com.au/Arduino/HardwareSerial9bit.zip.

This is a zip archive with 3 files in it:

  • HardwareSerial.cpp
  • HardwareSerial_private.h
  • HardwareSerial.h

You need to replace the existing files in your Arduino distribution for them to work. I strongly suggest saving the existing files first. In my case (Ubuntu) the appropriate folder was:

/usr/local/bin/arduino-1.6.9/hardware/arduino/avr/cores/arduino

Yours will probably be different, try going to the Arduino IDE directory and then doing a "find" for HardwareSerial.cpp.

This version uses more RAM as the transmit and receive buffers are now two bytes per character rather than one (to fit in the 9th bit).

Example code:

void setup ()
{
 Serial.begin (115200);  // debugging prints
 Serial1.begin (115200, SERIAL_8N1, true);  // 9 bit mode
 Serial2.begin (115200, SERIAL_8N1, true);  // 9 bit mode
 Serial.println ("--- starting ---");
}  // end of setup

int i;

void loop ()
{

 Serial1.write9bit (i++);  // send another byte
 
 // display incoming on Serial2
 if (Serial2.available ())
   Serial.println ((int) Serial2.read (), HEX);
   
 // check if we have sent all possible characters
 if (i >= 0x200)
   {
   delay (100);
   while (Serial2.available ())
     Serial.println ((int) Serial2.read (), HEX);
   delay (5000);
   i = 0;
   }  // end of sent 512 bytes
}  // end of loop

I tested on a Mega so I could use normal serial for debugging prints, and transmit from Serial1 to Serial2 for testing. To do this jumper together TX1 to RX2 (pins 17 and 18 on the Arduino Mega board).

@Nick Gammon, I have tested the library and it's helpful.

I was wondering, is it possible if I set the AVR internal register for 9-bit communication using util/setbaud.h and set the UCSRB & UCSRB for 9-bit communication will I be able to send/receive 9-bit data using Arduino functions Serial.Write and Serial.Read ? or I will have to receive the data while monitoring the registers UCSRA.

I don't understand that. Can you explain in more detail?

Hello Nick,

thank you for sharing your library. If I select Mega as board in actual Arduino IDE it seems to work for me, too. But I like to use the lib for board "ESP8266 Generic Modul".
With this board selected compiler comes with error messsage "no matching function for call to 'HardwareSerial::begin(int, SerialConfig, bool)' "
I thought actually that ESP8266 also uses the same HardwareSerial as Mega/Uno.
But it seems they are different. Do you think it's easy possible to adapt the lib for ESP8266, too?
To be honest I don't know how to adapt it.
I have also the HardwareSerial lib only in Arduino installation folder

\Arduino\hardware\arduino\avr\cores\arduino\

Indeed I've found on

the lib. But I don't know what exactly to do. It would be great if you can help me with this.
Thank you very much in advance!

donjuan

Hi Nick, I'm interested in carrying on the experimentation in

http://forum.arduino.cc/index.php?topic=282752.0

http://forum.arduino.cc/index.php?topic=39755.0

trying to fool an HU with DMS (Blaupunkt) protocol

to add an external audio source through cd changer connector.

Do you think your library that is a complete solution can handle that communication ? I guess Yes but pls confirm I'm pretty new in handling low lev comms. THANKS

Hi Nick,

I've recently been trying to read 9-bit data from a Raymarine ST60. I've built bi-directional circuit as detailed by Mr Knauf here:

The circuit seems to be fine and operates as expected. I've been trying to use the Arduino 1.6.9 IDE with your software locate here:

http://gammon.com.au/Arduino/HardwareSerial9bit.zip

The first problem that I encountered was the two missing manifests TXB80 and RXB80. To resolve this I defined them in HarwareSerial.h as:

#define TXB80 0
#define RXB80 1

At this point the code compiles cleanly with the board type set up "Arduino/Genuino Micro".

I'm opening the serial port as:

Serial1.begin(4800,SERIAL_8N1,true);

and I have a simple read loop that loops on:

if (true == Serial1.available ())
{

// Read the character.
int v = Serial1.read() & 0x1FF;

for(int i = 8; i >= 0; i--)
{
if (v & (1 << i))
Serial.print("1");
else
Serial.print("0");
}
Serial.print(" ");
Serial.print(v,HEX);
Serial.print(" ");
Serial.println(v);
}

The software seems to be reacting as expected. With the ST60 unplugged it is silent when I plug the ST60 in I get the following and it looks wrong compared to any details of Seatalk that I have seen!

000000000 0 0
110100111 1A7 423
111111111 1FF 511
111111111 1FF 511
000000000 0 0
001110111 77 119
011111111 FF 255
111111111 1FF 511
000000000 0 0
001111111 7F 127
011111101 FD 253
101101100 16C 364
101011011 15B 347
110101101 1AD 429

I was wondering if I was doing anything obviously wrong, or whether indeed, the board that I have chosen is supported?

Many thanks

Martin

Hi,

I finally came to make my Seatalk project. I started out here and downloaded Nick great code. Thanks Nick.
Then it was time to make the hardware to read the Seatalk data. And things did not compute. I got the wrong commands and it did not work.
Until I looked on a logic analyser (Analog discovery 2, great toy if you can get the edu discount ) the code was inverted. All the schematics I found did invert the signal.
I used: this but this one inverts! So I added an ttl inverter to the rx and tx in fact I did use a 74LS86B a 4 dual EXOR gates. By pulling one input of the EXOR high it inverts the other input on the output. If pulled low it does not invert. Very handy while experimenting with stuff like this. In the schematic R6 should be lower like 1.5K else the gate will not go low.

In the protocoll there is no end character. But after 10mS an other sender can take the line. On the logic analyser the most time between two bytes of the same message was 4.45ms. So I wait for 6mS to process the input buffer.
After 10mS no data the code can send commands to the bus.
It is only the first setup of the code:

/*
 * The seatalk receive circuit mut be NON-INVERTING there are many wrong circuits out there. I could only find the wrong versions! 
 * I used: http://berreizeta.blogspot.de/2016/10/blog-post.html but this one inverts! So I added an ttl inverter to the rx and tx.
 * in fact I did use a 74LS86B a 4 dual EXOR gates. By pulling one input of the EXOR high it inverts the other input on the output. If 
 * pulled low it does not invert. Very handy while experimenting with stuff like this. In the schematic R6 should be lower like 1.5K 
 * else the gate will not go low.
 * The code is my first setup but you get my drift
 */
#define SEATALKBUBLEN 20
char seaTalkBufRX[SEATALKBUBLEN]; //longest string found = 16 bytes
char seaTalkBufTX[SEATALKBUBLEN]; //longest string found = 16 bytes

void setup() {

  Serial.begin(115200);
  Serial1.begin(4800,SERIAL_8N1, true); //this is the only init possible if you leave out SERIAL_8N1 you get 8 bit!
  //The seatalk receive circuit mut be NON-INVERTING there are many wrong circuits out there. I could only find the wrong versions! 

}

void loop() {
  // put your main code here, to run repeatedly:
  int i;
  int n=0;
  unsigned long t1;
  t1=millis();
  while(1)
  {  
  while(Serial1.available())
  {
    t1=millis();
    i=Serial1.read();
    i=i&0x1ff; //if there is leftover data in the high byte delete this.
    if(i & 0x100)
      Serial.println(' ');
    Serial.print(i,HEX);
    Serial.print(' ');
    seaTalkBufRX[n]=i;
    n++;
  }
  if(millis()-t1>6) //there is no closing char. but the most time between bytes is 4.45ms (on my logic analyser) 6ms should be safe
    DoBufRX();
  if(millis()-t1>=10)
    SendSeatalk();
  }
}

void SendSeatalk(void)
{
  //here goes the send commands. 10ms no data on the bus is bus is free for data
}

ClearBuf(char * buf, int len)
{
  for(int i=0;i<len;i++)
    buf[i]=0;
}

void DoBufRX(void)
{
  //do your rx stuff here
  
}

I hope this helps making your seatalk project.

were you able to read data being sent from an actual seatalk device. Like Martin in the previous post, I also set this one and everything seemed to be working fine, but getting nonsense from the ST 60. Will try the non-investing approach next time on the boat.

Hey Nick just wanted to say thanks for writing this library!

It seems like I have the read working and I can see the incoming 9th bit from my device but when I try to write the char 0x85 my oscilloscope is showing a 0?

I did look through your code some and saw something about low baud rates, I am down at 1200 if that makes a difference?

Using Arduino Mega 2560 Arduino 1.8.5 (I will try using the version you posted as well in the meantime just to rule that out)
Serial1.write9bit(0x85);

Thanks again!

Update: tried using 1.6.9 to compile but still same issue not getting the 9th bit set when using the write9bit function.

Update2: Am I supposed to write Serial1.write9bit(0x185); in order to have the 9th bit set? I see on my oscilloscope 0x185 on the output now but I may be a little lost.

osc.PNG

I've installed the ZIP and added the CPP file but get, "prototype for 'void HardwareSerial::begin(long unsigned int, byte, bool)' does not match any in class 'HardwareSerial'"

It bombs out on:
 void HardwareSerial::begin(unsigned long baud, byte config, bool use9Bits) {...}
Your prototypes are:

   void begin(unsigned long baud) { begin(baud, SERIAL_8N1); }
   void begin(unsigned long, uint8_t, bool use9Bits = false);

Can anyone explain what's going on? I have limited experience overloading a function but it looks like the second of the begin() functions matches the hardware declaration.

Hello,

I use an Arduino Mega 2560 with IDE 1.8.10 and unfortunately the Nick Gammon's library for using 9 bits data (Seatalk interface) does not work anymore (though it once did for me).

With the updated library here :

http://gammon.com.au/Arduino/HardwareSerial9bit.zip

I've run Nick Gammon's test here :

void setup ()
{
 Serial.begin (115200);  // debugging prints
 Serial1.begin (115200, SERIAL_8N1, true);  // 9 bit mode
 Serial2.begin (115200, SERIAL_8N1, true);  // 9 bit mode
 Serial.println ("--- starting ---");
}  // end of setup

int i;

void loop ()
{

 Serial1.write9bit (i++);  // send another byte
 
 // display incoming on Serial2
 if (Serial2.available ())
   Serial.println ((int) Serial2.read (), HEX);
  
 // check if we have sent all possible characters
 if (i >= 0x200)
   {
   delay (100);
   while (Serial2.available ())
     Serial.println ((int) Serial2.read (), HEX);
   delay (5000);
   i = 0;
   }  // end of sent 512 bytes
}  // end of loop

and the result shows that the 9th bit is not processed :

12:16:26.166 -> F3
12:16:26.166 -> F4
12:16:26.166 -> F5
12:16:26.166 -> F6
12:16:26.166 -> F7
12:16:26.166 -> F8
12:16:26.166 -> F9
12:16:26.166 -> FA
12:16:26.166 -> FB
12:16:26.166 -> FC
12:16:26.166 -> FD
12:16:26.166 -> FE
12:16:26.166 -> FF
12:16:26.166 -> 0
12:16:26.166 -> 1
12:16:26.166 -> 2
12:16:26.166 -> 3
12:16:26.166 -> 4
12:16:26.166 -> 5
12:16:26.166 -> 6
12:16:26.166 -> 7
12:16:26.166 -> 8

Instead of showing 100 after FF it returns to 0 !

To discriminate between writing or reading fault I used the polling method described in the Arduino datasheet : the result is that the issue is not with the write9bit procedure but with the read procedure :

unsigned int USART2_Receive ( void )              // reception on 9 bits
{
  unsigned char status, resh, resl;
                                               // INIT RECEPTION on 9 bits 
   UCSR2B = (1 << UCSZ22) | (1<<RXEN2);         // enable REC + force 9 bits
                                               
   while ( !(UCSR2A & (1<<RXC2)));                // Wait for  data to be received 
  if ( UCSR2A & ((1<<FE2)|(1<<DOR2)|(1<<UPE2)) ) {USART2_flush();}
  resh = UCSR2B;                                  // Get 9th bit, then data from buffer                                                
  resl = UDR2;
  resh = (resh >> 1) & 0x01;                      //Filter the 9th bit, then return                                                 
  return ((resh << 8) | resl);
}

void USART2_flush(void)
{
  unsigned char dummy;
  while (UCSR2A & (1<<RXC2)) dummy = UDR2;
}



void setup ()
{
 Serial.begin (115200);  // debugging prints
 Serial1.begin (115200,SERIAL_8N1,true);  // 9 bit mode
 Serial2.begin (115200,SERIAL_8N1,true);  // 9 bit mode
 Serial.println ("--- starting ---");
}  // end of setup

int i;

void loop ()
{
  unsigned int d;
 
for (i=0;i<512;i++)
 {
  Serial1.write9bit (i);
 
  d = USART2_Receive ();
   Serial.print ("i = ");
   Serial.print (i,DEC);
   Serial.print (" = ");
   Serial.println (d,HEX);   
   delay (100);
 }
    
}  // end of loop

As we can see here

POLLING :

11:50:47.483 -> i = 244 = F4
11:50:47.623 -> i = 245 = F5
11:50:47.717 -> i = 246 = F6
11:50:47.811 -> i = 247 = F7
11:50:47.905 -> i = 248 = F8
11:50:47.998 -> i = 249 = F9
11:50:48.092 -> i = 250 = FA
11:50:48.186 -> i = 251 = FB
11:50:48.326 -> i = 252 = FC
11:50:48.420 -> i = 253 = FD
11:50:48.514 -> i = 254 = FE
11:50:48.608 -> i = 255 = FF
11:50:48.701 -> i = 256 = 100
11:50:48.795 -> i = 257 = 101
11:50:48.889 -> i = 258 = 102
11:50:49.030 -> i = 259 = 103
11:50:49.123 -> i = 260 = 104
11:50:49.217 -> i = 261 = 105
11:50:49.311 -> i = 262 = 106
11:50:49.405 -> i = 263 = 107
11:50:49.498 -> i = 264 = 108
11:50:49.639 -> i = 265 = 109
11:50:49.733 -> i = 266 = 10A
11:50:49.826 -> i = 267 = 10B

the 9 th bit has been well written with the write9bit procedure and properly read by the USARTx_receive () function. So what's wrong now with the reading function in the interrupt library ?

Of course my problem could be solved by using polling but my project is to make a multiplexer and I need reading through interrupts and not through polling for that.

EDIT : I've found a solution : going back to IDE 1.6.9 : it works fine and I am happy again !

Thank you Nick.

Friendly

JPT

Has anyone done anything with respect to the Due? I have seen implementations for Teensy 3.x which also based on ARM...

Hi,
I'm just getting started on a project to take wind data from a Raymarine Seatalk Instrument (I60) and convert it to NMEA 1083 for my other instruments.
I've just spent a lot of time getting the Nicks Library to work.
An observation that may save others some headache:
As mentioned above. Uninstall newer versions of the IDE and re install 1.6.9
BUT, (on my windows 7 install) you need to replace the HardwareSerial files at \AppData\Local\Arduino15\packages\arduino\hardware\avr\1.6.23\cores\arduino
Not as you would expect in the 1.6.9 Install folders or any folders remaining from the previous 'newer' version.

A good indication that all is well is when the 'SERIAL_8N1' parameter doesn't throw a compile error.