Blinking a coded error message with a single led

Hi guys,

I have a question regarding a way to implement a functionality that would blink a coded message using a single LED.

Some devices blink coded error using a 2 digits pattern, eg. 2 blinks, silence and 5 blinks would represent a the message 2-5.

I would like to use a table of blinking pattern so that when there's an error, the device fetch the right error code and blinks the pattern.

How would you implement this?

Here's my attempt, but I think it is not optimized.


const int ledPin =  LED_BUILTIN;// the number of the LED pin

byte codeLength = 2;
byte codePos = 0;
byte codeValue[] = {2, 5}; // Example of code

int blinkCount = 0;

int blinkTask(int dt) {
  static int blinkAcc = 0;
  static int blinkRate = 250;
  static int blinkState = 0x1;
  int blinkDone = 0x0; // Full cycle completed
  
  blinkAcc += dt;

  if (blinkAcc >= blinkRate) {
    blinkAcc = 0;
    
    blinkState = !blinkState;
    digitalWrite(ledPin, blinkState);
    blinkCount++;
    blinkDone = !(blinkCount & 1);
  }

  return blinkDone;
}

void ledMessageTask(int dt) {
  static unsigned int pauseAcc = 0;
  static int pauseRate = 1000;
  static enum codeState {BLINK, SILENCE} currentState = BLINK;
  static unsigned int cycleCount = 0;

  switch (currentState) {
    case BLINK:
      cycleCount += blinkTask(dt);
      
      if (cycleCount > codeValue[codePos]) {
        cycleCount = 0;
        codePos = (codePos + 1) % codeLength;
    
        currentState = SILENCE;
        digitalWrite(ledPin, 0);
      }
      break;
    case SILENCE:
      pauseAcc += dt;
      if (pauseAcc >= pauseRate) {
        pauseAcc = 0;
        blinkCount = 0;
        
        currentState = BLINK;
      }
  }
}

unsigned long cT; // currentTime
unsigned long pT; // previousTime
unsigned long dT; // deltaTime

void setup() {
  // set the digital pin as output:
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600);

  cT = pT = dT = 0;  
}

void loop() {
  cT = millis();
  dT = cT - pT;
  pT = cT;

  ledMessageTask(dT);  
}

How would you implement it?

What functionality do You experience from this code?

I would do the thing like I do with Serial.

I send sentences with the structure of "<command,data,data,data>" I set the Serial receiver to ignore all the incoming until it gets a sentence start. At sentence start I place the incoming into a buffer till a >, end of sentence, is received. I then send the received packet onto a parser.

void fReceiveSerial_LIDAR( void * parameters  )
{
  bool BeginSentence = false;
  char OneChar;
  char *str;
  str = (char *)ps_calloc(300, sizeof(char) ); // put str buffer into PSRAM
  // log_i("Free PSRAM before String: %d", ESP.getFreePsram());
  for ( ;; )
  {
    EventBits_t xbit = xEventGroupWaitBits (eg, evtReceiveSerial_LIDAR, pdTRUE, pdTRUE, portMAX_DELAY);
    if ( LIDARSerial.available() >= 1 )
    {
      while ( LIDARSerial.available() )
      {
        OneChar = LIDARSerial.read();
        if ( BeginSentence )
        {
          if ( OneChar == '>')
          {
            if ( xSemaphoreTake( sema_ParseLIDAR_ReceivedSerial, xSemaphoreTicksToWait10 ) == pdTRUE )
            {
               xQueueOverwrite( xQ_LIDAR_Display_INFO, ( void * ) &str );
              xEventGroupSetBits( eg, evtParseLIDAR_ReceivedSerial );
              //
            }
            BeginSentence = false;
            break;
          }
          strncat( str, &OneChar, 1 );
        }
        else
        {
          if ( OneChar == '<' )
          {
            strcpy( str, ""); // clear string buffer
            BeginSentence = true; // found beginning of sentence
          }
        }
      } //  while ( LIDARSerial.available() )
    } //if ( LIDARSerial.available() >= 1 )
    xSemaphoreGive( sema_ReceiveSerial_LIDAR );
    //        log_i( "fReceiveSerial_LIDAR " );
    //        log_i(uxTaskGetStackHighWaterMark( NULL ));
  }
  free(str);
  vTaskDelete( NULL );
} //void fParseSerial( void * parameters  )

Example code of a receive serial task.

It's working, but I'm looking if I can improve it or if there's something wrong about it.

Any code that does the job is good code! Maybe implementing different codes for different messages could be interesting. Store the different codes in an array and use the index into the array to select message!

I'm not sure that I understand what you mean. Could you elaborate?

I'll try.

Say you send a pattern of info.

PATTERN < the info to send.

The receiver is just receiving stuff.

So if the receiver starts receiving TERNPAT because a start point has not been designated.

If a structured message like "<bilnk,blink,notblink,notblink,blink>" is sent then not until a < is received will the receiver function start storing or buffering the incoming message. Then when a > is received the full message properly formatted in the correct order has been receivved.

I hope that helps if not do a search on the site for "serial input basics."

Ok I now understand better. In my case, there's no serial involved and no need for a start pattern. Maybe a quick and short blinking pattern could be use to signal the end of the pattern so the user don't misinterpret 2-5 for 5-2.

What I’ve done in the past, is implement a timed block sequence . e.g. 16 slots per message block, would allow a 4 flash set (possible 4 on, with 4 off slots per message, = 8 slots plus the 8-slot ‘off’ gap between messages.

This can be stored in an unsigned 16-bit integer, and shifted out with a simple function.
I did it with two colour LEDs (red & green, two ints), so 2^3 * 3 colours == 48 unique codes.

e.g. Red off Green off Red off Yellow off, followed by 8 ‘off’ slots as a spacer, then repeat as necessary.

Of course, this can be simplified to a single colour.
Blink speed is up to you.
You can also do other things like set all slots on, to get the user’s attention, then blink out the sequence.

Remember to make the blink sequencer non-blocking, so your main code can keep doing what it’s doing.

Think of using a multidimensional array. At index 1 there is one message. At index 2 there's an other message. That way You only need one function to send 2 different messages.

In psedocode:

Delare
int messageArray[nrOfMessages, messageLenght];

(define the on and off sequence)

send (messageArray(x)).......

Something along the line

int dictionary[][] = {
{1, 2}, //> Network error
{1, 3}, //> SD Card error
// ...
{5, 2}, //> MQTT Server not responding
// ...
};

Is it that?

@ el_pablo
Yes, better and more correct.

And is there a "best practices" to store dictionary like that is optimized for the memory or flash?

Using byte, or word, binary coding the blinks, 0 or 1, would be less memory using but the transmission needs a shift out function.

Actually, I'm using bytes, but for the sake of simplicity I used int for the reply.

Byte, word/int or long.... It's all about the length of the message.

Traditionally this would be "Morse" code or International Morse Code which uses four symbols; a single length "mark" (called "dit"), a triple length "mark" (called "dah"), a single length "space" which separates marks in a character and a triple length "space" which separates different characters.

It is specifically designed to be human-readable. It makes sense to use this existing standard; you can assign the single letters (and numerals, which are longer) to a given message or code a complete message.