Controlling AC Voltage using arduino through serial.read

Hi, i am working with arduino and an app to control ac voltage going to some load. I am using optocoupler , moc and triac. Everything works fine, except one glitch probably with arduino side. When i send one byte number from bluetooth, with a slider button then volt meter shows 150 volts at maximum thumb position. And when i move slider to lower values voltage also decreases as expected but when i reach minimum vlaue of slider, the voltage goes abruptly to 200 volts. That means during normal operation of slider, i lost 50 volts which is not expected but this behavior self corrects sometime when power fluctuates. But i want proper and exact control as pe my app. It seems that there is some shift and loss of synchronization with AC signal detected by optocouplor. Any :light_bulb:ideas?

int mydelay = 0;
int myvalue=0;
int last_CH1_state = 0;
 
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
  pinMode(3,OUTPUT);        //Define D3 as output for the DIAC pulse
  Serial.begin(9600);       //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)
   { 
      myvalue = map(Serial.read(),0,255,10000,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)
{
  /////////////////////////////////////               //Input from optocoupler
  if(PINB & B00000001){                               //We make an AND with the pin state register, We verify if pin 8 is HIGH???
    if(last_CH1_state == 0){                          //If the last state was 0, then we have a state change...
      mydelay=1;                                    //We haev detected a state change!
    }
  }
  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
    }
}

Your topic does not indicate a problem with IDE 2.x and therefore has been moved to a more suitable location on the forum.

Perhaps some editing of the byte you just read needs to be done.

Without an annotated schematic showing how you have connected everything I can only take a SWAG (Scientific Will Assuming Guess) and say the pot is opening up. Best to post the annotated schematic so we can see what we are trying to solve.

Suggestion: check what is actually being sent.

This method of communication has a terrible flaw: there is no way to check that the received data are valid. For a better approach, have a look at the Serial Input Basics tutorial.

"Play" with the maximum value of your delay. 10000 μseconds is practically a theoretical value because the frequency of the electrical network is not exact, on average it is 50Hz (or 60Hz).
For example, for 50Hz I had to use a value close to 8000 to get good results and prevent the triac from triggering at maximum.

if(last_CH1_state == 1){

last_CH1_state never is set to 1.
You should check the code.

Yes that is true but I do not know how to detect that change when it becomes 1. Pin-8 is used for interrupt and pin 3 goes high and low as per program and it is connected to MOC3051 IC. interrupt pin 8 is connected to optocoupler to detect state change of AC signal. here i do not know how these variables are used to detect that change.

I want TRIAC to give me voltage from minimum to maximum as I send numbers from 1 to 255. and when I send a text to arduino to control some other pin it should not affect the rest functionality of TRIAC but it does.

Which "other pin"? Your code doesn't control any other pins.
Post the full code.

Add:

Your trigger routine, when running in loop(), is very sensitive to other tasks.
You should use a timer and trigger the triac from the interrupt generated by it.

This issue looks like it's caused by a synchronization problem between your Arduino and the AC zero-crossing signal from the optocoupler. Specifically:

When the slider sends 0 (minimum value), your mapped delay becomes 10000µs, which is half the AC cycle (for 50Hz).

If the timing isn't perfectly synced with the zero crossing, your TRIAC might fire during the next half-cycle, causing unexpected high voltage output, like the jump to 200V you're seeing.

And your interrupt (ISR(PCINT0_vect)) doesn’t reliably track rising/falling edges. It checks PINB & B00000001, but doesn't update last_CH1_state properly. That might be why mydelay gets triggered inconsistently.

Update the ISR like this:

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

This ensures mydelay is only set on the rising edge, keeping things in sync.

That is why i am confused as i have not defined any other pin. Why is that when i send some text "ON" or "OFF"and Arduino processes it.

Okay, let's clear things up a bit.

You said

Now you say

Your code #1 does not handle any pin other than the triac and does not process any message except the value of the byte it receives via serial with the map() function.

Yes, but as I will update my code later i will define that pin which should go HIGH or LOW based on what I send. But here even without defining a piN arduino processes it . I think it may be due the ISR signals activated on some ard. port. How to avoid this and make my code more controlled.

Your code will "process" anything that comes in the serial port. That includes numbers, characters, punctuation marks, carriage returns line feeds and more.

You have to make your code determine if it is a number or "ON" or "OFF"

I had similar behavior when played with zero crossing detection. The reason was that the zero crossing signal was very wide, in range of 0.5ms.
So it you use falling edge detection your timing is off by 200us and at minimum voltage setting the triac trigger goes to beginning of next half cycle.
Another problem with falling edge was that sometimes I got false signal from raising edge oscillation.
Best results I got with raising edge (debouncing applied) and 200us correction for timing.

Also, you could limit the delays to 500us - 9500us range, I don't see any use for values smaller or higher.

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)
   {
      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(250);
      digitalWrite(3,LOW);
      mydelay=0;
    }
}

As per your suggestion I managed to get lower value at zero button position by adjustig the timing as you said. With this idea I saved 40 volts of output from getting wasted, but now the difference remains just 10 volts of AC at the output and now I must try to figure out why this much voltage is not reflected at the output as it did previously though erratically. I think now there needs some fine tuning to be done or If you have another idea . You can share and thanks for your suggestion.

Quite likely.