AC Phase Control By Tweaking PWM Output to trigger Triac

why are you using a PWM signal? how do you synchronize it to the zero crossing?

knowing when there is a zero crossing, why don't you simply generate a short (1 usec) pulse after an appropriate delay to trigger the Triac?

// trigger line power triac with 25% duty cycle

#define ZeroCrPin     A1
#define TriacPin      10

#if 1
# define HalfPeriodUsec  (1000000/120)

#else   // values to test at human speeds with LED
# define PulseWidthMsec     250
# define HalfPeriodUsec     1000000
#endif
#define TriacDutyCycle(x)   (HalfPeriodUsec * (100-x)/100)


byte          zc;
byte          zcLst;
unsigned long zcUsec    = 0;
unsigned long triacUsec;

char s [90];

// -----------------------------------------------------------------------------
void loop (void)
{
    unsigned long usec = micros();

    // capture zero crossing
    zc = digitalRead (ZeroCrPin);
    if (zcLst != zc)  {
        zcLst = zc;

        if (LOW == zc)  {
            zcUsec = usec;
        }
    }

    // wait for triac delay
    if (zcUsec && (usec - zcUsec) > triacUsec)  {
        zcUsec = 0;

        digitalWrite (TriacPin, HIGH);
#ifdef PulseWidthMsec           // no need to ~usec pulse
        delay (PulseWidthMsec);
#endif
        digitalWrite (TriacPin, LOW);
    }
}

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

    digitalWrite (TriacPin, LOW);
    pinMode (TriacPin, OUTPUT);

    pinMode (ZeroCrPin, INPUT_PULLUP);
    zcLst = digitalRead (ZeroCrPin);

    triacUsec = TriacDutyCycle(25);

    sprintf (s, "%s: HalfPeriodUsec %ld, triacUsec %ld",
            __func__, HalfPeriodUsec, triacUsec);
    Serial.println (s);
}