Hardware serial port

Hi,
I was trying to define a PIN as OUTPUT to control LED on pin-11, but I am having an interrupt routine in my code that runs as I send one byte number from 1 - 255 from a slider button. I want to control LED on Pin-11 with another button that sends "N" to make LED on and sends "F" to make it off. The issue I am facing with my code is that when I send "N" to arduino, LED turns ON but i have to press it a number of times, and same is the case when I try to OFF this LED . It takes several button taps to make it OFF. When I tried this code without Interrupt routine , It worked normally. And this Led Button also interferes with the slider operation. Could anyone give some idea what extra needs to be done to resolve this issue.
here is my code:

int mydelay = 0;
int myvalue=0;
int last_CH1_state = 0;
int Received = 0;
int led1 = 11;
int led1_state = 0;
unsigned long ledtime;
unsigned long currenttime;
void setup() {
  /*
   * Port registers allow for lower-level and faster manipulation of the i/o pins of the microcontroller on an Arduino board.
   * The chips used on the Arduino board (the ATmega8 and ATmega168) have three ports:
     -B (digital pin 8 to 13)
     -C (analog input pins)
     -D (digital pins 0 to 7)  
  //All Arduino (Atmega) digital pins are inputs when you begin...
  */  
  
  PCICR |= (1 << PCIE0);    //enable PCMSK0 scan                                                
  PCMSK0 |= (1 << PCINT0);  //Set pin D8 trigger an interrupt on state change. Input from optocoupler
  Serial.begin(9600);
  pinMode(3,OUTPUT); //Define D3 as output for the DIAC pulse
  pinMode(led1,OUTPUT);
  digitalWrite(led1,LOW);
         
         //Start serial com with the BT module (RX and TX pins)
}
 
void loop()
{ 
   //Read the value of the pot and map it from 10 to 10.000 us. AC frequency is 50Hz, so period is 20ms. We want to control the power
   //of each half period, so the maximum is 10ms or 10.000us. In my case I've maped it up to 7.200us since 10.000 was too much
   if(Serial.available()>0)
   
   {

    Received = Serial.read();
    char rec = char(Received);
if (rec != '0')
    {
    Serial.println(rec); //This is to visualise the received character
    }
  }
  if (led1_state == 0 && Received == 'N') 
  {
    
   digitalWrite(led1, HIGH);   // turn the LED on (HIGH is the voltage level)
   
  led1_state =1;
  Received=0;
  
  }
  else if (led1_state == 1  && Received == 'F')
  {
    
  digitalWrite(led1, LOW);
  
  led1_state =0;
  Received=0;
  }

    
  {
      myvalue = map(Serial.read(),0,255,6700,10);
      //In my case I've used myvalue = map(Serial.read(),0,255,7000,10); for better results
  }
    if (mydelay)
    {
      delayMicroseconds(myvalue); //This delay controls the power
      digitalWrite(3,HIGH);
      delayMicroseconds(100);
      digitalWrite(3,LOW);
      mydelay=0;
    }

 
}

 
 
//This is the interruption routine
//----------------------------------------------

ISR(PCINT0_vect) {
  bool currentState = PINB & B00000001;
  if (currentState != last_CH1_state) {
    last_CH1_state = currentState;
    if (currentState == HIGH) {
      mydelay = 1;
    }
  }
  else if(last_CH1_state == 1){                       //If pin 8 is LOW and the last state was HIGH then we have a state change      
    mydelay=1;                                      //We haev detected a state change!
    last_CH1_state = 0;                               //Store the current state into the last state for the next loop
    }
}

As your topic does not relate directly to the installation or operation of the IDE it has been moved to the Programming category of the forum

Imagine that you send 'N' or 'F' just before the above statement is executed. It will be mapped to your myvalue en not be handled by Received = Serial.read();; hence you have to press it a number of times and it's the luck of the draw if it's handled correctly or not.

Any reading of serial should be inside the if(Serial.available()>0) block and you should only read once. Next you can determine if it's 'N' or 'F' or 1..255 and map.

Be aware that 'N' and 'F' also fall in the range 1..255 and hence those slider values will be handled as if you did send 'N' or 'F'.

All character fall in this range from bigining to the end , them what I should use to handle it. I need two buttons a slider with range 1-255 and a button to handle LED on/off.

Do you have to send binary values or can you use text?

With text it's reasonably easy and you can have a look at Robin's updated Serial Input Basics. Example 5 is probably your best bet but study the topic.

With example 5 you can e.g. send something like <S,value> for the slider and <B, 0> for off and <B, 1> for on. S and B indicate what was sending (slider or button).

If it has to be binary it becomes more complicated. You can send in a format like sliderValue buttonValue checksum (no spaces) and you will need a mechanism to synchronise.

Just now , I got another interesting idea which is , I will skip some slider values and reserve them for my LED button. This should solve my issue. Thanks for your reply.

why are you reading the serial input, Serial.read() in 2 locations? How do you know which statement is actually reading the char just entered?

look this over

int mydelay         = 0;
int myvalue         = 0;
int last_CH1_state  = 0;

const int PinDiac   = 3;
const int PinLed    = 11;

enum { LedOn = HIGH, LedOff = LOW };

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

    PCICR  |= (1 << PCIE0);    //enable PCMSK0 scan
    PCMSK0 |= (1 << PCINT0);  //Set pin D8 trigger an interrupt on state change. Input from optocoupler

    pinMode       (PinDiac, OUTPUT); //Define D3 as output for the DIAC pulse
    pinMode       (PinLed,  OUTPUT);
    digitalWrite  (PinDiac, HIGH);
    digitalWrite  (PinLed,  LedOff);
}

// -----------------------------------------------------------------------------
void loop ()
{
    if (Serial.available()) {
        char rec = Serial.read ();

        switch (rec)  {
        case '\n':
            break;

        case 'F':
            digitalWrite (PinLed, LedOff);
            break;

        case 'N':
            digitalWrite (PinLed, LedOn);
            break;

        default:
            myvalue = map (rec, 0, 255, 6700, 10);
            Serial.print   ("  myvalue ");
            Serial.println (myvalue);
            break;
        }
    }

    if (mydelay) {
        delayMicroseconds (myvalue); //This delay controls the power
        digitalWrite      (PinDiac, HIGH);
        delayMicroseconds (100);
        digitalWrite      (PinDiac, LOW);
        mydelay=0;
    }
}

// -----------------------------------------------------------------------------
ISR (PCINT0_vect) {
    bool currentState = PINB & B00000001;
    if (currentState != last_CH1_state) {
        last_CH1_state = currentState;
        if (currentState == HIGH) {
            mydelay = 1;
        }
    }

    // If pin 8 is LOW and the last state was HIGH then we have a state change
    else if (last_CH1_state == 1)  {
        mydelay        = 1;  // We haev detected a state change!
        last_CH1_state = 0;  //Store the current state into the last state for the next loop
    }
}
1 Like

Can you reduce the range to 2-255 (or 1-254) so that Off will be 0 and On will be 1 (or 255) ? myvalue = map (rec, **2**, 255, 6700, 10); will normalize (expand) the range back...

ascii chars are in the range of 0-127.

i don't think it really has any effect if you treat 'F' and 'N' differently, not using those chars for computing myvalue

This seems to be the best solution, but here you read only character not numbers.

 if (Serial.available()) {
        char rec = Serial.read ();

yes , I am going to reduce the range for two numbers.

where are you reading numbers other than the values of the ASCII chars. the ASCII value of '0' is 32. the ASCII value of 'F' 70

So you can use @gcjr's code but with this modification:

if (Serial.available()) {
        char rec = Serial.read (); // char is equivalent to byte and uint8_t

        switch (rec)  {
        case 0:
            digitalWrite (PinLed, LedOff);
            break;

        case 1:
            digitalWrite (PinLed, LedOn);
            break;

        default:
            myvalue = map (rec, 2, 255, 6700, 10);
            Serial.print   ("  myvalue ");
            Serial.println (myvalue);
            break;
        }
    }

in which case, you don't send ASCII but binaries

1 Like

how do you enter the value of 0 thru the serial port which is receiving ASCII characters (see chart above)? hint: it's not the '0' key.

1 Like

Here is your codes output when i send 254 and 255
myvalue 5389
myvalue 5310
myvalue 5336
myvalue 5389
myvalue 5310
myvalue 5310

From reading this:

I think @74younus is using another Arduino to send the char/value.

@74younus Are you using a serial terminal or an Arduino to send the char?

i am using an APP that can send 0 or 1 in two ways as a text or as a number

How does your system behave if you send a value (0-255) as a number?
We need something whose equivalent Arduino code would be:

Serial.write(value);

Thanks @gcjr @Etienne_74 and for your support and for providing the solution . Issue resolved with these codes.
I just reduced the slide range but not impact on the output which was required. the output got mapped to the shortened range of the slider. every thing is fine now thanks again.

Please tell us more as it could interest others in a similar situation.
Was it the "as a number" parameter that solved the issue ?

1 Like