NewSoftSerial like mad

Here's a simple little project, but it shows off Mikal Hart's NewSoftSerial library rather well.

It reads out the current and voltage applied to an electrically-heated crucible, and calculates and displays the power and resistance. If the heating element has any defects, the resistance will increase over time. The display modules are from Sparkfun and controlled by four soft-serial ports (Watts + milliOhms x 2 channels).

Well done,

Can you post some code too?

Sure. I was hoping to show the hardware nicely installed in a box too, but that'll have to wait until the workshop reopens.

As for the code, well there's nothing too devastating to see (this is compiled with the IDE v1.0 beta-1, by the way). I'm new to the "++" part of C++ and was happy to discover that I could create an array of NewSoftSerial instances, even though the constructor takes parameters (line 9). That's important when you're running five serial ports.

dodis() initialises the displays, no matter what baud rate they're set to. This device is operating in a very noisy environment, and occasionally the interference causes one or more displays to switch their baud rate. Since baud rate configuration is stored in non-volatile memory, the software has to deal with it even after a reset.

dodat() displays a floating point number as best it can, within the limits of the 4-digit display.

#include <SoftwareSerial.h>
#include <Streaming.h>

#define CONDUCTANCE_CH1 100 // current scale: amps per volt [Ohm^-1]
#define CONDUCTANCE_CH2 60  // current scale: amps per volt [Ohm^-1]
#define SCALE_V 1.0 // voltage-divider ratio on voltage (read on pins A0, A1, A2, A3)
#define SCALE_I 0.5 // voltage-divider ratio on current (read on pins A4, A5)

SoftwareSerial dis[4] = { SoftwareSerial(2,3),   // Ch. 1 : Power display: TX = pin-3
                         SoftwareSerial(4,5),   // Ch. 1 : Resistance display: TX = pin-5
                         SoftwareSerial(6,7),   // Ch. 2 : Power display: TX = pin-7
                         SoftwareSerial(8,9) }; // Ch. 2 : Resistance display: TX = pin-9

void setup() {
  pinMode(A0, INPUT); digitalWrite(A0, LOW);
  pinMode(A1, INPUT); digitalWrite(A1, LOW);
  pinMode(A2, INPUT); digitalWrite(A2, LOW);
  pinMode(A3, INPUT); digitalWrite(A3, LOW);
  pinMode(A4, INPUT); digitalWrite(A4, LOW);
  pinMode(A5, INPUT); digitalWrite(A5, LOW);
  pinMode(13, OUTPUT);

  dodis();
  Serial.begin(19200);
}


void loop() {
  int a[6]; // adcs
  int adcmode, n, quit;
  float w[6]; // voltage inputs
  float v[2], i[2], p[2], r[2]; // power and resistance
  float adcref[4];
 
  Serial << endl;

  adcref[DEFAULT] = 5.0;
  adcref[INTERNAL] = 1.1;
  adcmode=DEFAULT; // try DEFAULT=5V, then INTERNAL=1.1V, then EXTERNAL=quit

  quit=0; while( !quit ) {
    analogReference(adcmode); // set ADC full-scale reference voltage
    digitalWrite(13,HIGH); delay(10);  digitalWrite(13,LOW); delay(10); // flash during reads; also 20ms delay after changing Aref to charge decoupling cap
    for ( n=0; n<6; n++ ) {analogRead(n); } // dummy reads whilst reference stabilises
    
    for ( n=0; n<6; n++ ) {
      a[n]=analogRead(n);
      w[n]=a[n]; w[n]*=adcref[adcmode]/1024;
      if (a[n]>200) quit=1; // quit loop early if any value exceeds scale crossover at 200/1024*5V = 0.98V
    }
    
    if ( adcmode == INTERNAL) {quit=1;} else {adcmode=INTERNAL;}
  }

  Serial << "a0 " << a[0] << " / a1 " << a[1] << " / a2 " << a[2] << " / a3 " << a[3] << " / a2 " << a[4] << " / a3 " << a[5] << endl;
  Serial << "V0 " << w[0] << " / V1 " << w[1] << " / V2 " << w[2] << " / V3 " << w[3] << " / V2 " << w[4] << " / V3 " << w[5] << endl;

  float c[] = { CONDUCTANCE_CH1, CONDUCTANCE_CH2 };
  for ( n=0; n<2; n++ ) {
    i[n]=w[4+n]*c[n]/SCALE_I;
    v[n]=abs(w[0+n]-w[2+n])/SCALE_V;
    p[n]=v[n]*i[n];      // power [W]
    r[n]=v[n]/i[n]*1e3;  // resistance [mOhm]
    dodat(dis[0+n], p[n]);  // units: Watts
    dodat(dis[2+n], r[n]);  // units: milli-Ohm
    Serial << "Current " << i[n] << " [A] / Voltage " << v[n]*1e3 << " [mV] / Power " << p[n] << " [W] / Resistance " << r[n] << " [mOhm]" << endl;
  }

  delay(500);
}


void dodis() { // set baud, no matter what it's set to at the begining
  char buf[] = "    HELLO    vvvv\x7F\001";
  long baud[] = { 2400, 4800, 9600, 14400, 19200, 38400, 57600 };
  int m,n;
   
  for (n=0; n<4; n++) {
    dis[n].begin(57600);
    dis[n] << "vvvv    z" << '\255' << "w" << '\000'; // reset; blank; brightness=min; decimal=off
    dis[n].end();
  }
  
  for (m=0; m<6; m++) {
    for (n=0; n<4; n++) {
      dis[n].begin(baud[m]);
      buf[18] = m+1;
      dis[n] << (buf+13); // reset; set baud
      dis[n].end();
    }
    delay(100);
  }
  
  for (n=0; n<4; n++) {
    dis[n].begin(57600);
    dis[n] << "vvvv    z" << '\000' << "w" << '\000'; // reset; blank; brightness=full; decimal=off
  }
  
  for (m=0; m<10; m++) {
    char c=buf[m+4]; buf[m+4]=0;
    for (n=0; n<4; n++) { dis[n] << "v" << (buf+m); } // scroll "HELLO"
    delay(100);
    buf[m+4]=c;
  }

  for (n=0; n<4; n++) { dis[n] << "z\xFFv0000"; } // min. brightness, '0000'
  m=255; while( m>0 ) { // fade in: 127-0
    m>>=1; delay(100);
    for (n=0; n<4; n++) { dis[n] << "z" << m; }
  }

}


void dodat( SoftwareSerial &d, float f) { // print floating-pint number
  int dec, i;
  char buf[8];

  if (f<10) {dec=1; i=f*1e3+0.5;}
  else if (f<100) {dec=2; i=f*1e2+0.5;}
  else if (f<1000) {dec=4; i=f*1e1+0.5;}
  else if (f<10000) {dec=8; i=f+0.5;}

  if (f<10000) {sprintf(buf,"%c%04u",dec,i);}
  else {sprintf(buf,"%cHIGH",0x80);}
  
  d << "vw" << buf; // reset; decimals; digits
  
//  Serial << '<' << (buf+1) << '>' << endl;
}

If I understand your code correctly you never read from the NewSoftSerial ports. Then you are allowed to define them as

SoftwareSerial dis[4] = { SoftwareSerial(-1,3),   // Ch. 1 : Power display: TX = pin-3
                         SoftwareSerial(-1,5),   // Ch. 1 : Resistance display: TX = pin-5
                         SoftwareSerial(-1,7),   // Ch. 2 : Power display: TX = pin-7
                         SoftwareSerial(-1,9) }; // Ch. 2 : Resistance display: TX = pin-9

to free some pins...

I wondered about that. Thanks for the tip.