Photon to Photon 2 migration issue

I'm really disappointed with the Photon 2. I've been using the old spark core and old photon for DIY projects since they came out. I'm trying migrate from to the Photon 2 and it is a totally different device. Already fried 1 trying to get it working. Less I/O, drives less current, and my code won't run on it.

I use 4 analog inputs and 1 PWM output. My PWM output is jumping like it is being stomped on. Doesn't matter which output I try to use. I see the value briefly but it gets stomped and the voltage drops. AI can't figure it out. Is there some glaring difference with the Photon 2 around analog inputs and outputs? My code base is to much to post on here.

The hardware is quite different. Be sure to read the Photon 2 from Photon migration guide carefully.

Among the potential issues from your post:

  • Photon 2 (also Argon) is not 5V tolerant but Photon 1 is
  • Not all pins in positions you'd think they'd be ADC on the Photon 2 are actually ADC ports
  • The drive strength is lower but you can increase it somewhat using setPinDriveStrength
  • The GPIO on the P2/Photon 2 is significantly slower; this will affect anything that requires manually controlling the GPIO at a high speed ("bit-banging")

Thanks. I read the migration guide and was aware of all of this. I removed the photon 2 from my board and am testing without any loads. Though if I get it working a fear it won't be able to drive my mosfet. The last one is what I'm thinking may be related to my issue. If I'm reading 4 analog inputs can the photon 2 not handle writing to the 1 PWM output as well?

Does the photon 2 have trouble switching between analog reads and writes quickly?

There are 4 easily usable ADC inputs on the Photon 2, but beware of the pin that's in the position you'd think A3 would be in, which is actually A5. Pins A0 - A2 are normal ADC.

There is no problem using 3 ADC and 1 PWM. Since you need all of the ADC, the only PWM compatible pins that can be used are MOSI, MISO, and D1.

All PWM pins use the a single timer, including the RGB LED. This is usually fine with a single PWM, but can be an issue with multiple PWM, since they all must share the same frequency. Each PWM can have a different duty cycle.

If you need to switch between PWM output and ADC input on a single pin, you'll probably be limited in how quickly you can switch back and forth. On different pins you'll be fine.

I'm using A0,A1,A2, and A5 for inputs and they all seemed to be working. I'm trying to use D1(Also known as A4) for my PWM output. But I've test my code on other PWM outputs and have the same problem on all of them. I'm not trying to switch between reading and writing on the same pin. Not switching at all. Just 4 pins that are always reading and 1 pin that is always writing. The behavior of the PWM output looks like programming issue but I can load this same code on a photon 1 and it works fine. The only change to the code I'm making is the pin assignments. I think the problem is more to do with something else that is different between the photon 1 and 2 but I can't put my finger on it. My program works fine for reading the inputs and I can load simple program to control the PWM output and it works fine. But when I run the full program the PWM output misbehaves. My program uses several libraries so there is more going on than I can troubleshoot. Would it help if I upload a zip of my entire program if that is even possible on here?

In your full program, is there anything that enables I2C (Wire)? Since pin D1 is also an I2C pin, if Wire.begin() is called anywhere in your code or in system code, the pin will stop working as PWM. On the Photon 2 there shouldn't be anything in the system that does, but on some other platforms like B-SoM its used for PMIC and fuel gauge.

I came across the Wire.begin() conflict with D1 also being an I2C pin and actually did have one line calling that. But it wasn't in use as I had started with a project that used a display but never implemented the display. I thought that was going to be the smoking gun but alas that did not fix the problem. I really think it is something like that going on though. Well I take that back. I've tried moving my PWM output around to the other pins and they all have the same behavior so that rules out the IC2 conflict on D1. Would SYSTEM_THREAD(ENABLED); or UDP communication disrupt PWM output?

Also wondering if maybe I'm assigning a pin that doesn't exist on the photon 2 that did exist on the photon 1 but something that wasn't actually being used.

I know my code is horrible and it came in a little funky on this post but it has been cooking mighty fine brisket for 6 years! Here is the main file. Maybe you can spot something that is incompatible with the photon 2.

#include "PID_AutoTune.h"
#include "PID.h"
#include "SmartProbe.h"
#include "SmartBbq.h"
// #include "Adafruit_LEDBackpack.h"
#include "Button.h"
#include "ThermistorProbe.h"
#include "SimpleLed.h"
#include "math.h"
#include  // std::iota
// #include <blynk.h>
#define PROBE1 A0
#define PROBE2 A1
#define PROBE3 A2
#define PROBE4 A5
#define PROBEINDICATOR1 D7
#define PROBEINDICATOR2 D6
#define PROBEINDICATOR3 D5
#define PROBEINDICATOR4 D4
// #define BUTTON_LEFT A2
// #define BUTTON_CENTER D3
// #define BUTTON_RIGHT D2
#define FAN1 D0 //Not actually used
#define FAN2 A4

TParameters Parameters; //for saving to eeprom. See smartSprinkler SmartControl.h to further define this
// int FAN2 = D1;
int Speed = 0;
#define LEDCYCLE 300U
#define ALARMSETDELAY 800U
#define DEBUG FALSE

// ==================== UDP SETTINGS ====================
UDP udp;
IPAddress ignitionIP(192, 168, 0, 43);   // <<< CHANGE TO YOUR IGNITION GATEWAY IP
const int UDP_SEND_PORT = 7123;          // Ignition receives status here
const int UDP_RECEIVE_PORT = 7124;       // Photon listens for commands here
// ====================================================

char response\[1024\];

int hue;
int saturation;
int brightness;
int red;
int green;
int blue;
char rgbTimeString\[128\];
char ledTimeString\[43\];
bool V5, V16, V17, V18, V19, V33, V34, V35, V36, V51, V52, V54;
int V1, V2, V3, V4, V6, V7, V9, V11, V12, V13, V14, V15, V20, V21, V22, V23, V24, V28, V30
, V40, V44, V45, V46, V47, V50, V53, V55, V56, V57, V59, V58, V60;
int V10;
double V8, V25, V26, V27, V31, V32, V37, V38, V39, V41, V42, V43, V48, V49;
unsigned long V29;

// API variables
char revision[ ] = "1.0";
double probeTemp1 = 10000.0;
double probeTemp2 = 10000.0;
double probeTemp3 = 10000.0;
double probeTemp4 = 10000.0;
double prevInput = 0.0;
double inputRate = 0.0;
double avgInputRate = 0.0;
double probeAlarmHI1 = 0.0;
double probeAlarmHI2 = 0.0;
double probeAlarmHI3 = 0.0;
double probeAlarmHI4 = 0.0;
double probeAlarmLO1 = 0.0;
double probeAlarmLO2 = 0.0;
double probeAlarmLO3 = 0.0;
double probeAlarmLO4 = 0.0;
bool probeAlarmE1 = FALSE;
bool probeAlarmE2 = FALSE;
bool probeAlarmE3 = FALSE;
bool probeAlarmE4 = FALSE;
bool setTemp325 = FALSE;
float setProbeAlarmHI1 = 0.0;
float setProbeAlarmHI2 = 0.0;
float setProbeAlarmHI3 = 0.0;
float setProbeAlarmHI4 = 0.0;
float setProbeAlarmLO1 = 0.0;
float setProbeAlarmLO2 = 0.0;
float setProbeAlarmLO3 = 0.0;
float setProbeAlarmLO4 = 0.0;
bool probeAlarmAct1 = 0;
bool probeAlarmAct2 = 0;
bool probeAlarmAct3 = 0;
bool probeAlarmAct4 = 0;
int probeAlarmState1 = 4;
int probeAlarmState2 = 0;
int probeAlarmState3 = 0;
int probeAlarmState4 = 0;
int blynkSetpoint = 0;
int blynkSetpointPrev = 0;
unsigned long fanBump = millis();
unsigned long testTime = millis();
int list\[500\]; //list for poulating menu widget
char myword[ ] = { 'H', 'e', 'l', 'l', 'o'};
char auth[ ] = "ZzyVPSOwp\_-ZvRKntzyeDTPnP_ctwUwt"; //for local PI server
bool notified = FALSE;
bool tune = 0;
bool notifiedTuneComp = 0;
bool fanPulsedOn = FALSE;
unsigned long fanOffTime = 0;
unsigned long fanOnTime = 0;
bool alarmActive1 = 0;
unsigned long alarmTimeSinceActive = 0;
unsigned long alarmTimeRepeat = 0;
int probeAlarmState1Prev = 4;
int probeAlarmState2Prev = 0;
int probeAlarmState3Prev = 0;
int probeAlarmState4Prev = 0;
bool temp3Notified = 0;
bool temp4Notified = 0;
//Autotune
byte ATuneModeRemember=2;
double input=80, output=0, setpoint=165;
double displayedOutput = 0;
double prevOutput = 0.0;
double kpmodel=1.8, taup=1800, theta\[1200\];
double outputStart=150;
double aTuneStep=60, aTuneNoise=10, aTuneStartValue=50;
unsigned int aTuneLookBack=1800;
boolean tuning = false;
unsigned long modelTime, serialTime;
boolean AutoMode = true;
PID myPID(&input, &output, &setpoint,Parameters.kp,Parameters.ki,Parameters.kd, DIRECT);
PID_ATune aTune(&input, &output);
double rateTestValue = 0;
double rateTestPreviousValue = 0;
double rateTestRate = 0;
double rateTestSetpoint = 0;
double rateTestAvgRate = 0;
double tmToSetpoint = 0;
double tmToSetpointAvg = 0;
float cookAvg = 0;
float cookTot = 0;
unsigned long cookNumSamples;
const int probe1RunAvgCount = 16;
double probe1RunAvgBuffer\[probe1RunAvgCount\];
int probe1NextRunAvg = 0;
double probe1RunAvg = 0;
double probe1Fltr = 10000;
int tempMenu\[68\];
int pitAlmMenu\[30\];
int meatAlmMenu\[27\];
bool lidOpen = 0;
bool fanEnabled = 0;
unsigned long lidOpenDelay = 30;
unsigned long timeLidOpen = 0;
double probe1Deviation = 0;
int almCycle = 1;
double A, B, C;
double R1, R2, R3, T1, T2, T3;
bool alarmToggle = 0;
bool probeAlarmAckd1 = 1;
bool probeAlarmRepeat1 = 0;
bool probeAlarmAckd2 = 1;
bool probeAlarmRepeat2 = 1;
bool probeAlarmAckd3 = 1;
bool probeAlarmRepeat3 = 1;
bool probeAlarmAckd4 = 1;
bool probeAlarmRepeat4 = 1;
bool setpointAdj = 0;
unsigned long setPointAdjStart = 0;
bool probeAlarmAckdSetpoint = 1;
bool setpointAlarmEna = 0;
bool probeAlarmSetpoint = 0;
unsigned long ms1000 = 0;
unsigned long ms10000 = 0;
int watchDog = 1;
float msLastCycle = 0;
int testTemp = A5;
int modelTimeSP = 1000;
bool lastTune = 0;
static bool lastSimulation = false;
SmartBbq bbq(PROBE1,PROBE2,PROBE3,PROBE4,PROBEINDICATOR1,PROBEINDICATOR2,PROBEINDICATOR3,PROBEINDICATOR4,FAN1,FAN2);
SYSTEM_THREAD(ENABLED);
void setup() {
pinMode(FAN2, OUTPUT);
// pinSetDriveStrength(FAN1, DriveStrength::HIGH);
myPID.SetMode(AUTOMATIC);

Serial.begin(9600);
delay(5000);
Serial.println("Serial test");

V36 = HIGH;
V52 = LOW;
bbq.setProbeAlarmLO(0,Parameters.setpoint - 5);
bbq.setProbeAlarmHI(0,Parameters.setpoint + 5);
V19 = 1;
bbq.setProbeAlarmEna(0,1);
bbq.setProbeAlarmEna(1,1);
bbq.setProbeAlarmEna(2,1);
bbq.setProbeAlarmEna(3,1);
setpointAlarmEna = 1;

for(byte i=0;i<=67;i++) {
    tempMenu[i]=i*5 + 165;
}
for(byte i=0;i<=29;i++) {
    pitAlmMenu[i]=i + 1;
}

for(byte i=0;i<=26;i++) {
    meatAlmMenu[i]=i*5 + 100;
}

if(Parameters.useSimulation) {
    for(int i=0;i<1200;i++) {
      theta[i]=outputStart;
    }
    modelTime = 0;
}
if(tuning) {
    tuning=false;
    changeAutoTune();
    tuning=true;
}

serialTime = 0;
Serial.begin(9600);

EEPROM.get(0, Parameters);
if (Parameters.Magic != 0xA5A5) {
    Parameters.Magic = 0xA5A5;
    Parameters.fanEnabled = false;
    Parameters.setpoint = 0;
    Parameters.pitAlm = 20;
    Parameters.meat1Alm = 145;
    Parameters.meat2Alm = 145;
    Parameters.meat3Alm = 145;
    Parameters.kp = 1.25;
    Parameters.ki = 0.005;
    Parameters.kd = 5;
    Parameters.useSimulation = false;
    Parameters.testTempOffset = 0;
    EEPROM.put(0, Parameters);
}
V51 = Parameters.fanEnabled;
V53 = ((Parameters.setpoint - 165)/5)+1;
setpoint = Parameters.setpoint;
V20 = Parameters.pitAlm;
V21 = Parameters.meat1Alm;
V22 = Parameters.meat2Alm;
V23 = Parameters.meat3Alm;
bbq.setProbeAlarmHI(0,Parameters.setpoint + Parameters.pitAlm);
bbq.setProbeAlarmLO(0,Parameters.setpoint - Parameters.pitAlm);
bbq.setProbeAlarmHI(1,meatAlmMenu[Parameters.meat1Alm-1]);
bbq.setProbeAlarmHI(2,meatAlmMenu[Parameters.meat2Alm-1]);
bbq.setProbeAlarmHI(3,meatAlmMenu[Parameters.meat3Alm-1]);
V25 = Parameters.kp;
V26 = Parameters.ki;
V27 = Parameters.kd;
V35 = Parameters.useSimulation;
V50 = Parameters.testTempOffset;
myPID.SetTunings(Parameters.kp,Parameters.ki,Parameters.kd);
myPID.SetSampleTime(1000);

// UDP Setup
udp.begin(UDP_RECEIVE_PORT);
Serial.print("UDP started - listening on port ");
Serial.println(UDP_RECEIVE_PORT);
}
void loop() {
bool controlTick = false;
if (millis() - ms1000 >= 1000) {
ms1000 = millis();
blynkSend();
lidOpenDetect();
watchDog = 0;
controlTick = true;
}
if (millis() - ms10000 >= 1000) {
ms10000 = millis();
blynkSendSlow();
alarmNotify();
}
probe1RunAvgBuffer[probe1NextRunAvg++] = probeTemp1;
if (probe1NextRunAvg >= probe1RunAvgCount) {
    probe1NextRunAvg = 0;
}
probe1RunAvg = 0;
for(int i=0; i< probe1RunAvgCount; ++i) {
    probe1RunAvg += probe1RunAvgBuffer[i];
}
probe1RunAvg /= probe1RunAvgCount;
probe1Deviation = abs(int(round(bbq.getProbeTemp(0))) - int(round(probe1Fltr)));
if (probe1Fltr == 10000) {
    probe1Fltr = bbq.getProbeTemp(0);
}
else if (probe1Deviation < 25 && Parameters.testTempOffset == 0) {
    probe1Fltr += ((bbq.getProbeTemp(0) - probe1Fltr) * .0125);
}
else if (probe1Deviation < 25 && Parameters.testTempOffset != 0) {
    probe1Fltr += (((bbq.getProbeTemp(0) + Parameters.testTempOffset) - (probe1Fltr + Parameters.testTempOffset) * .0125));
}
else {
    if (int(round(bbq.getProbeTemp(0))) == -16) {
        probe1Fltr = 9999;
    }
    else {
        probe1Fltr = bbq.getProbeTemp(0) + Parameters.testTempOffset;
    }
    probeTemp1 = bbq.getProbeTemp(0);
}
if (probeTemp2 == 10000) {
    probeTemp2 = bbq.getProbeTemp(1);
}
else if (abs(int(round(bbq.getProbeTemp(1))) - int(round(probeTemp2))) < 25 && Parameters.testTempOffset == 0) {
    probeTemp2 += ((bbq.getProbeTemp(1) - probeTemp2) * 0.125);
}
else {
    if (int(round(bbq.getProbeTemp(1))) == -16) {
        probeTemp2 = 9999;
    }
    else {
        probeTemp2 = bbq.getProbeTemp(1) + Parameters.testTempOffset;
    }
}

if (probeTemp3 == 10000) {
    probeTemp3 = bbq.getProbeTemp(2);
}
else if (abs(int(round(bbq.getProbeTemp(2))) - int(round(probeTemp3))) < 25 && Parameters.testTempOffset == 0) {
    probeTemp3 += (bbq.getProbeTemp(2) - probeTemp3) * 0.125;
}
else {
    if (int(round(bbq.getProbeTemp(2))) == -16) {
        probeTemp3 = 9999;
    }
    else {
        probeTemp3 = bbq.getProbeTemp(2) + Parameters.testTempOffset;
    }
}

if (probeTemp4 == 10000) {
    probeTemp4 = bbq.getProbeTemp(3);
}
else if (abs(int(round(bbq.getProbeTemp(3))) - int(round(probeTemp4))) < 25 && Parameters.testTempOffset == 0) {
    probeTemp4 += (bbq.getProbeTemp(3) - probeTemp4) * 0.125;
}
else {
    if (int(round(bbq.getProbeTemp(3))) == -16) {
        probeTemp4 = 9999;
    }
    else {
        probeTemp4 = bbq.getProbeTemp(3) + Parameters.testTempOffset;
    }
}
probeAlarmHI1 = bbq.getProbeAlarmHI(0);
probeAlarmHI2 = bbq.getProbeAlarmHI(1);
probeAlarmHI3 = bbq.getProbeAlarmHI(2);
probeAlarmHI4 = bbq.getProbeAlarmHI(3);

probeAlarmLO1 = bbq.getProbeAlarmLO(0);
probeAlarmLO2 = bbq.getProbeAlarmLO(1);
probeAlarmLO3 = bbq.getProbeAlarmLO(2);
probeAlarmLO4 = bbq.getProbeAlarmLO(3);

probeAlarmE1 = bbq.getProbeAlarmE(0);
probeAlarmE2 = bbq.getProbeAlarmE(1);
probeAlarmE3 = bbq.getProbeAlarmE(2);
probeAlarmE4 = bbq.getProbeAlarmE(3);

probeAlarmAct1 = bbq.getProbeAlarmAct(0);
probeAlarmAct2 = bbq.getProbeAlarmAct(1);
probeAlarmAct3 = bbq.getProbeAlarmAct(2);
probeAlarmAct4 = bbq.getProbeAlarmAct(3);

probeAlarmState1 = bbq.getProbeAlarmState(0,probe1Fltr);
probeAlarmState2 = bbq.getProbeAlarmState(1,probeTemp2);
probeAlarmState3 = bbq.getProbeAlarmState(2,probeTemp3);
probeAlarmState4 = bbq.getProbeAlarmState(3,probeTemp4);

Speed = 200;
delay(1);

unsigned long now = millis();

if(!Parameters.useSimulation) {
    input = probe1Fltr;
}
if (controlTick == 1){
    if(tuning) {
        byte val = (aTune.Runtime());
        if (val!=0) {
            tuning = false;
        }
        if(!tuning) {
            Parameters.kp = aTune.GetKp();
            Parameters.ki = aTune.GetKi();
            Parameters.kd = aTune.GetKd();
            myPID.SetTunings(Parameters.kp,Parameters.ki,Parameters.kd);
            AutoTuneHelper(false);
        }
    }
    else myPID.Compute();
}
if(Parameters.useSimulation) {
    if (!lastSimulation) {
        input = 200.0;
        probe1Fltr = 200.0;
        for(int i = 0; i < 1200; i++) {
            theta[i] = outputStart;
        }
        output = outputStart;
        Serial.println("Simulation initialized to steady 200°F");
    }
    lastSimulation = true ;
  
    theta[600]=output;
  
    if(now>=modelTime) {
        modelTimeSP = V58;
        modelTime += modelTimeSP;
        DoModel();
    }
  
    probe1Fltr = input;
}
else {
    lastSimulation = false;
    if (Parameters.fanEnabled == 1 && lidOpen == 0) {
        if (probeTemp1 > -60) {
            if (output >= 20 && output < 50) {
                if (millis() - fanBump < 5000) {
                    analogWrite(FAN2,50);
                }
                else {
                    analogWrite(FAN2,output);
                }
            }
            else if (output >= 50) {
                analogWrite(FAN2,output);
            }
            else if (output < 20) {
                fanBump = millis();
                if (output > 0) {
                    int pulseDutyOn = output / 50 * 10000;
                    int pulseDutyOff = 10000 - pulseDutyOn;
                    if (fanPulsedOn == FALSE) {
                        if (fanOffTime < millis()) {
                            analogWrite(FAN2,50);
                            fanOnTime = millis() + pulseDutyOn;
                            fanPulsedOn = TRUE;
                        }
                    }
                    else {
                        if (fanOnTime < millis()) {
                            analogWrite(FAN2,0);
                            fanOffTime = millis() + pulseDutyOff;
                            fanPulsedOn = FALSE;
                        }
                    }
                }
                else {
                    analogWrite(FAN2,0);
                }
            }
            testTime = millis();
        }
        else {
            analogWrite(FAN2,0);
        }
    }
    else {
        analogWrite(FAN2,0);
    }
}

if (setpointAdj == 1) {
    if (probe1Fltr > Parameters.setpoint - Parameters.pitAlm && probe1Fltr < Parameters.setpoint + Parameters.pitAlm) {
        bbq.setProbeAlarmLO(0,Parameters.setpoint - Parameters.pitAlm);
        bbq.setProbeAlarmHI(0,Parameters.setpoint + Parameters.pitAlm);
        setpointAdj = 0;
        probeAlarmAckdSetpoint = 1;
        V54 = LOW;
    }
}

// UDP send
if (controlTick) {
    sendUDPStatus();
}

// UDP receive with full parsing
checkUDPCommands();
}
void sendUDPStatus() {
char msg\[520\];
snprintf(msg, sizeof(msg),
"PHOTON_DATA,V1=%d,V2=%d,V3=%d,V4=%d,V5=%d,V6=%d,V7=%d,V8=%.2f,V9=%d,"
"V11=%d,V12=%d,V13=%d,V14=%d,V15=%d,V16=%d,V17=%d,V18=%d,V19=%d,"
"V20=%d,V21=%d,V22=%d,V23=%d,V24=%d,V25=%.4f,V26=%.4f,V27=%.4f,"
"V28=%d,V30=%d,V31=%.2f,V32=%.2f,V33=%d,V34=%d,V35=%d,V36=%d,"
"V40=%d,V41=%.2f,V42=%.2f,V43=%.2f,V44=%d,V45=%d,V46=%d,V47=%d,"
"V48=%.2f,V49=%.1f,V50=%d,V51=%d,V52=%d,V53=%d,V54=%d,V55=%d,"
"V58=%d,V59=%d,V60=%d",
V1, V2, V3, V4, V5?1:0, V6, V7, V8, V9,
V11, V12, V13, V14, V15, V16?1:0, V17?1:0, V18?1:0, V19?1:0,
V20, V21, V22, V23, V24, V25, V26, V27,
V28, V30, V31, V32, V33?1:0, V34?1:0, V35?1:0, V36?1:0,
V40, V41, V42, V43, V44, V45, V46, V47,
V48, V49, V50, V51?1:0, V52?1:0, V53, V54?1:0, V55,
V58, V59, V60);
// Serial.print("Sending UDP status... ");
int result = udp.sendPacket((const uint8_t*)msg, strlen(msg), ignitionIP, UDP_SEND_PORT);

    if (result == -26 || result < 0) {
        Serial.println("UDP socket error detected - restarting UDP...");
        udp.begin(UDP_RECEIVE_PORT);   // this resets the socket
    }
}
void checkUDPCommands() {
int packetSize = udp.parsePacket();
if (packetSize > 0) {
char packetBuffer\[256\];
int len = udp.read(packetBuffer, 255);
if (len > 0) {
packetBuffer\[len\] = '\\0';
        Serial.print("*** RECEIVED: ");
        Serial.println(packetBuffer);

        String command = String(packetBuffer);

        if (command.startsWith("SET,")) {
            String params = command.substring(4);  // remove "SET,"
            int start = 0;
            bool receivedCmd = false;

            while (start < params.length()) {
                int comma = params.indexOf(',', start);
                if (comma == -1) comma = params.length();
                
                String param = params.substring(start, comma);
                int eq = param.indexOf('=');
                
                if (eq > 0) {
                    String name = param.substring(0, eq);
                    String value = param.substring(eq + 1);

                    // === YOUR ORIGINAL PARSING LOGIC (kept exactly) ===
                    if (name == "V1") { V1 = value.toInt(); receivedCmd = true; }
                    if (name == "V2") { V2 = value.toInt(); receivedCmd = true; }
                    if (name == "V3") { V3 = value.toInt(); receivedCmd = true; }
                    if (name == "V4") { V4 = value.toInt(); receivedCmd = true; }
                    if (name == "V5") { V5 = (value.toInt() != 0); receivedCmd = true; }
                    if (name == "V6") { V6 = value.toInt(); receivedCmd = true; }
                    if (name == "V7") { V7 = value.toInt(); receivedCmd = true; }
                    if (name == "V8") { V8 = value.toFloat(); receivedCmd = true; }
                    if (name == "V9") { V9 = value.toInt(); receivedCmd = true; }
                    if (name == "V10") { V10 = value.toInt(); receivedCmd = true; }
                    if (name == "V11") { V11 = value.toInt(); receivedCmd = true; }
                    if (name == "V12") { V12 = value.toInt(); receivedCmd = true; }
                    if (name == "V13") { V13 = value.toInt(); receivedCmd = true; }
                    if (name == "V14") { V14 = value.toInt(); receivedCmd = true; }
                    if (name == "V15") { V15 = value.toInt(); receivedCmd = true; }
                    if (name == "V16") { V16 = (value.toInt() != 0); receivedCmd = true; }
                    if (name == "V17") { V17 = (value.toInt() != 0); receivedCmd = true; }
                    if (name == "V18") { V18 = (value.toInt() != 0); receivedCmd = true; }
                    if (name == "V19") { V19 = (value.toInt() != 0); receivedCmd = true; }
                    if (name == "V20") { V20 = value.toInt(); receivedCmd = true; }
                    if (name == "V21") { V21 = value.toInt(); receivedCmd = true; }
                    if (name == "V22") { V22 = value.toInt(); receivedCmd = true; }
                    if (name == "V23") { V23 = value.toInt(); receivedCmd = true; }
                    if (name == "V24") { V24 = value.toInt(); receivedCmd = true; }
                    if (name == "V25") { V25 = value.toFloat(); receivedCmd = true; }
                    if (name == "V26") { V26 = value.toFloat(); receivedCmd = true; }
                    if (name == "V27") { V27 = value.toFloat(); receivedCmd = true; }
                    if (name == "V28") { V28 = value.toInt(); receivedCmd = true; }
                    if (name == "V29") { V29 = strtoul(value.c_str(), NULL, 10); receivedCmd = true; }
                    if (name == "V30") { V30 = value.toInt(); receivedCmd = true; }
                    if (name == "V31") { V31 = value.toFloat(); receivedCmd = true; }
                    if (name == "V32") { V32 = value.toFloat(); receivedCmd = true; }
                    if (name == "V33") { V33 = (value.toInt() != 0); receivedCmd = true; }
                    if (name == "V34") { V34 = (value.toInt() != 0); receivedCmd = true; }
                    if (name == "V35") { V35 = (value.toInt() != 0); receivedCmd = true; }
                    if (name == "V36") { V36 = (value.toInt() != 0); receivedCmd = true; }
                    if (name == "V37") { V37 = value.toFloat(); receivedCmd = true; }
                    if (name == "V38") { V38 = value.toFloat(); receivedCmd = true; }
                    if (name == "V39") { V39 = value.toFloat(); receivedCmd = true; }
                    if (name == "V40") { V40 = value.toInt(); receivedCmd = true; }
                    if (name == "V41") { V41 = value.toFloat(); receivedCmd = true; }
                    if (name == "V42") { V42 = value.toFloat(); receivedCmd = true; }
                    if (name == "V43") { V43 = value.toFloat(); receivedCmd = true; }
                    if (name == "V44") { V44 = value.toInt(); receivedCmd = true; }
                    if (name == "V45") { V45 = value.toInt(); receivedCmd = true; }
                    if (name == "V46") { V46 = value.toInt(); receivedCmd = true; }
                    if (name == "V47") { V47 = value.toInt(); receivedCmd = true; }
                    if (name == "V48") { V48 = value.toFloat(); receivedCmd = true; }
                    if (name == "V49") { V49 = value.toFloat(); receivedCmd = true; }
                    if (name == "V50") { V50 = value.toInt(); receivedCmd = true; }
                    if (name == "V51") { V51 = (value.toInt() != 0); receivedCmd = true; }
                    if (name == "V52") { V52 = (value.toInt() != 0); receivedCmd = true; }
                    if (name == "V53") { V53 = value.toInt(); receivedCmd = true; }
                    if (name == "V54") { V54 = (value.toInt() != 0); receivedCmd = true; }
                    if (name == "V55") { V55 = value.toInt(); receivedCmd = true; }
                    if (name == "V56") { V56 = value.toInt(); receivedCmd = true; }
                    if (name == "V57") { V57 = value.toInt(); receivedCmd = true; }
                    if (name == "V58") { V58 = value.toInt(); receivedCmd = true; }
                    if (name == "V59") { 
                        V59 = value.toInt(); 
                        receivedCmd = true; 
                        V59 = 0;  // Watchdog reset
                    }
                    if (name == "V60") { V60 = value.toInt(); receivedCmd = true; }

                    if (receivedCmd) {
                        Serial.print("Updated ");
                        Serial.print(name);
                        Serial.print(" = ");
                        Serial.println(value);
                    }
                }
                start = comma + 1;
            }
        }
    }
}
}
void alarmNotify() {
almCycle = 1;


V28 = almCycle;
V29 = millis() - alarmTimeRepeat;

if (almCycle == 1) {
    if (lidOpen == 0) {
        if ((probeAlarmState1Prev != probeAlarmState1) || (probeAlarmRepeat1 == 1)) {
            probeAlarmAckd1 = 0;
            if (probeAlarmState1 > 2) {
                probeAlarmRepeat1 = 0;
                V54 = 0;
                almCycle = 0;
            }
            else {
                almCycle = 2;
            }
            probeAlarmState1Prev = probeAlarmState1;
        }
        else {
            almCycle = 2;
        }
    }
    else {
        almCycle = 2;
    }
}
if (almCycle == 2) {
    if ((probeAlarmState2Prev != probeAlarmState2) || (probeAlarmRepeat2 == 1)) {
        probeAlarmAckd2 = 0;
        if (probeAlarmState2 > 0 && probeAlarmState2 < 4) {
            probeAlarmRepeat2 = 0;
            V54 = LOW;
            almCycle = 0;
        }
        else {
            almCycle = 3;
        }
        probeAlarmState2Prev = probeAlarmState2;
    }
    else {
        almCycle = 3;
    }
}
if (almCycle == 3) {
    if ((probeAlarmState3Prev != probeAlarmState3) || (probeAlarmRepeat3 == 1)) {
        probeAlarmAckd3 = 0;
        if (probeAlarmState3 > 0 && probeAlarmState3 < 4) {
            probeAlarmRepeat3 = 0;
            V54 = LOW;
            almCycle = 0;
        }
        else {
            almCycle = 4;
        }
        probeAlarmState3Prev = probeAlarmState3;
    }
    else {
        almCycle = 4;
    }
}


if (almCycle == 4) {
    if ((probeAlarmState4Prev != probeAlarmState4) || (probeAlarmRepeat4 == 1)) {
        probeAlarmAckd4 = 0;
        if (probeAlarmState4 > 0 && probeAlarmState4 < 4) {
            probeAlarmRepeat4 = 0;
            V54 = 0;
        }
        probeAlarmState4Prev = probeAlarmState4;
    }
}
if (probeAlarmState1 < 3
    && (probeAlarmState2 == 0 || probeAlarmState2 == 4)
    && (probeAlarmState3 == 0 || probeAlarmState3 == 4)
    && (probeAlarmState4 == 0 || probeAlarmState4 == 4)
    && (probeAlarmAckdSetpoint == 0)) {
    alarmTimeSinceActive = millis();
    alarmTimeRepeat = millis();
    V54 = LOW;
}

if (millis() - alarmTimeRepeat > 300000) {
    if (millis() - alarmTimeSinceActive > 99999999) {
        alarmTimeRepeat = millis();
    }
    else {
        if (probeAlarmState1 > 2 && probeAlarmAckd1 == 0) {
            probeAlarmRepeat1 = 1;
        }
        if (probeAlarmState2 > 0 && probeAlarmState2 < 4 && probeAlarmAckd2 == 0) {
            probeAlarmRepeat2 = 1;
        }
        if (probeAlarmState3 > 0 && probeAlarmState3 < 4 && probeAlarmAckd3 == 0) {
            probeAlarmRepeat3 = 1;
        }
        if (probeAlarmState4 > 0 && probeAlarmState4 < 4 && probeAlarmAckd4 == 0) {
            probeAlarmRepeat4 = 1;
        }
        alarmTimeRepeat = millis();
    }
}
if (setpointAdj == 1) {
    if (probeAlarmAckdSetpoint == 0 && setpointAlarmEna == 1) {
        if (setPointAdjStart == 0) {
            setPointAdjStart = millis();
        }
        if (millis() - setPointAdjStart > 1800000 && probeAlarmAckdSetpoint != 1) {
            setPointAdjStart = millis() - 300000;
            probeAlarmAckdSetpoint = 1;
        }
    }
}
else {
    setPointAdjStart = 0;
    probeAlarmAckdSetpoint = 0;
}


}

void lidOpenDetect() {
if (lidOpen == 1) {
if (((millis() - timeLidOpen) > (lidOpenDelay\*1000)) && (input > Parameters.setpoint)) {
V52 = LOW;
lidOpen = 0;
V36 = 1;
AutoMode = 1;
myPID.SetMode(AUTOMATIC);
}
if ((millis() - timeLidOpen) > 600000) {
V52 = LOW;
lidOpen = 0;
V36 = 1;
AutoMode = 1;
myPID.SetMode(AUTOMATIC);
}
}
else {
timeLidOpen = millis();
}
}

void blynkSend() {
double tempBBQ = probeTemp1;
V1 = int(Parameters.setpoint);
if (Parameters.fanEnabled == 1 && lidOpen == 0) {
displayedOutput = output;
}
else {
displayedOutput = 0;
}
V2 = int(round((displayedOutput)*100/255));
V3 = int(round(probe1Fltr));
V4 = int(round(probeTemp2));
V6 = int(round(probeTemp3));
V7 = int(round(probeTemp4));
V60 = int(round(probeTemp4));
V8 = probe1Deviation;
V9 = int(round(bbq.getProbeTemp(0)));
V30 = Parameters.setpoint;
V31 = input;
V59 = watchDog;
V56 = analogRead(testTemp);
inputRate = (probe1Fltr - prevInput);
prevInput = probe1Fltr;
avgInputRate += (inputRate - avgInputRate) \* .01;
V48 = avgInputRate \* 60;
tmToSetpoint = (Parameters.setpoint - probe1Fltr) / (avgInputRate*60);
tmToSetpointAvg += (tmToSetpoint - tmToSetpointAvg) \* 0.9;
if (tmToSetpoint < 0) {
V49 = 9999;
}
else {
V49 = tmToSetpointAvg;
}


if (output != prevOutput) {
    V32 = output;
    prevOutput = output;
    if (tuning == 0) {
        if (notifiedTuneComp == 0) {
            notifiedTuneComp = 1;
        }
    }
    else {
        notifiedTuneComp = 0;
    }
}
if (alarmToggle == 0) {
    alarmToggle = 1;
}
else {
    alarmToggle = 0;
}
if (V19 == 1) {
    bbq.setProbeAlarmEna(0,1);
    bbq.setProbeAlarmEna(1,1);
    bbq.setProbeAlarmEna(2,1);
    bbq.setProbeAlarmEna(3,1);
    setpointAlarmEna = 1;
}
else {
    bbq.setProbeAlarmEna(0,0);
    bbq.setProbeAlarmEna(1,0);
    bbq.setProbeAlarmEna(2,0);
    bbq.setProbeAlarmEna(3,0);
    setpointAlarmEna = 0;
    V54 = LOW;
}
tune = V33;
if (tune == 1 && lastTune == 0) {
    changeAutoTune();
}
else if (tune == 0 && tuning == 1) {
    changeAutoTune();
}
lastTune = tune;

AutoMode = V36;
if (AutoMode == 1) {
    myPID.SetMode(AUTOMATIC);
}
else {
    myPID.SetMode(MANUAL);
}

output = V32;

if (V54 == 1) {
    if (probeAlarmState1 > 2) {
        probeAlarmAckd1 = 1;
        probeAlarmRepeat1 = 0;
    }
    if (probeAlarmState2 > 0 && probeAlarmState2 < 4) {
        probeAlarmAckd2 = 1;
        probeAlarmRepeat2 = 0;
    }
    if (probeAlarmState3 > 0 && probeAlarmState3 < 4) {
        probeAlarmAckd3 = 1;
        probeAlarmRepeat3 = 0;
    }
    if (probeAlarmState4 > 0 && probeAlarmState4 < 4) {
        probeAlarmAckd4 = 1;
        probeAlarmRepeat4 = 0;
    }
    if (setpointAdj == 1) {
        probeAlarmAckdSetpoint = 1;
    }
    V54 = 0;
}
 if (lidOpen != V52) {
    lidOpen = V52;
    if (lidOpen == 1) {
        V36 = 0;
        AutoMode = 0;
        myPID.SetMode(MANUAL);
    }
    else {
        V36 = 1;
        AutoMode = 1;
        myPID.SetMode(AUTOMATIC);
    }
}

int prevSetpoint = Parameters.setpoint;
if (setpoint < prevSetpoint) {
    bbq.setProbeAlarmLO(0,setpoint - Parameters.pitAlm);
}
else if (setpoint > prevSetpoint) {
    bbq.setProbeAlarmHI(0,setpoint + Parameters.pitAlm);
}
setpointAdj = 1;
probeAlarmAckdSetpoint = 0;

T1 = V37;
T2 = V38;
T3 = V39;

bool updateEEPROM = 0;
if (V51 != Parameters.fanEnabled) {
    Parameters.fanEnabled = V51;
    updateEEPROM = 1;
}
if (V53 != ((Parameters.setpoint - 165)/5)+1) {
    int tempMenuIndex = V53;
    setpoint = Parameters.setpoint = tempMenu[tempMenuIndex-1];
    updateEEPROM = 1;
}
if (V20 != Parameters.pitAlm) {
    int tempMenuIndex = V20;
    Parameters.pitAlm = pitAlmMenu[tempMenuIndex-1];
    bbq.setProbeAlarmLO(0,Parameters.setpoint - Parameters.pitAlm);
    bbq.setProbeAlarmHI(0,Parameters.setpoint + Parameters.pitAlm);
    updateEEPROM = 1;
}
if (V21 != Parameters.meat1Alm) {
    int tempMenuIndex = V21;
    bbq.setProbeAlarmHI(1,meatAlmMenu[tempMenuIndex-1]);
    Parameters.meat1Alm = tempMenuIndex;
    updateEEPROM = 1;
}
if (V22 != Parameters.meat2Alm) {
    int tempMenuIndex = V22;
    bbq.setProbeAlarmHI(2,meatAlmMenu[tempMenuIndex-1]);
    Parameters.meat2Alm = tempMenuIndex;
    updateEEPROM = 1;
}
if (V23 != Parameters.meat3Alm) {
    int tempMenuIndex = V23;
    bbq.setProbeAlarmHI(3,meatAlmMenu[tempMenuIndex-1]);
    Parameters.meat3Alm = tempMenuIndex;
    updateEEPROM = 1;
}
if (V25 != Parameters.kp) {
    if (tune == 0) {
        Parameters.kp = V25;
        updateEEPROM = 1;
        myPID.SetTunings(Parameters.kp,Parameters.ki,Parameters.kd);
    }
    else {
        V25 = Parameters.kp;
        updateEEPROM = 1;
    }
}
if (V26 != Parameters.ki) {
    if (tune == 0) {
        Parameters.ki = V26;
        updateEEPROM = 1;
        myPID.SetTunings(Parameters.kp,Parameters.ki,Parameters.kd);
    }
    else {
        V26 = Parameters.ki;
        updateEEPROM = 1;
    }
}
if (V27 != Parameters.kd) {
    if (tune == 0) {
        Parameters.kd = V27;
        updateEEPROM = 1;
        myPID.SetTunings(Parameters.kp,Parameters.ki,Parameters.kd);
    }
    else {
        V27 = Parameters.kd;
        updateEEPROM = 1;
    }
}
if (V35 != Parameters.useSimulation) {
    Parameters.useSimulation = V35;
    updateEEPROM = 1;
}
if (V50 != Parameters.testTempOffset) {
    int testTempOffset = V50;
    Parameters.testTempOffset = testTempOffset;
    updateEEPROM = 1;
}
if (updateEEPROM == 1) {
    EEPROM.put(0, Parameters);
    updateEEPROM = 0;
}
V24 = int(System.freeMemory());


}

void blynkSendSlow() {
V5 = notified;
V11 = probeAlarmE1;
V12 = int(probeAlarmHI1);
V13 = int(probeAlarmHI2);
V14 = int(probeAlarmHI3);
V15 = int(probeAlarmHI4);
V16 = probeAlarmE2;
V17 = probeAlarmE3;
V18 = probeAlarmE4;
V33 = tune;
V34 = tuning;
V35 = Parameters.useSimulation;
V40 = probeAlarmState1;
V41 = probeAlarmState2;
V42 = probeAlarmState3;
V43 = probeAlarmState4;
V44 = int(probeAlarmLO1);
V45 = int(probeAlarmLO2);
V46 = int(probeAlarmLO3);
V47 = int(probeAlarmLO4);
V58 = modelTimeSP;
if (Parameters.fanEnabled == 0) {
cookAvg = probe1Fltr;
V55 = 0;
cookNumSamples = 1;
}
else {
cookAvg += (probe1Fltr - cookAvg) / cookNumSamples;
V55 = int(round(cookAvg));
cookNumSamples++;
}
}

void changeAutoTune() {
if(!tuning) {
aTuneStartValue = output;
aTune.SetNoiseBand(aTuneNoise);
aTune.SetOutputStep(aTuneStep);
aTune.SetLookbackSec((int)aTuneLookBack);
AutoTuneHelper(true);
tuning = true;
}
else {
aTune.Cancel();
tuning = false;
AutoTuneHelper(false);
}
}

void AutoTuneHelper(boolean start) {
if(start)
ATuneModeRemember = myPID.GetMode();
else
myPID.SetMode(ATuneModeRemember);
}

void SerialSend() {
Serial.print("setpoint: ");Serial.print(Parameters.setpoint); Serial.print(" ");
Serial.print("input: ");Serial.print(input); Serial.print(" ");
Serial.print("output: ");Serial.print(output); Serial.print(" ");
if(tuning) {
Serial.println("tuning mode");
} else {
Serial.print("kp: ");Serial.print(myPID.GetKp());Serial.print(" ");
Serial.print("ki: ");Serial.print(myPID.GetKi());Serial.print(" ");
Serial.print("kd: ");Serial.print(myPID.GetKd());Serial.println();
}
}

void SerialReceive() {
if(Serial.available()) {
char b = Serial.read();
Serial.flush();
if((b=='1' && !tuning) || (b!='1' && tuning)) changeAutoTune();
}
}

void DoModel() {
for (int i = 0; i < 1199; i++) {
theta\[i\] = theta\[i + 1\];
}
const double T_eq = 200.0;
const double a = 1.0 - 1.0 / taup;
const double b = kpmodel / taup;
input = a \* input + b \* (theta\[0\] - outputStart) + (1.0 - a) \* T_eq;
}

There's too much code to spot anything unusual, I'd try reducing the code to a simpler fail case.

To make sure there isn't an issue with using A4/D1 in particular in PWM mode, I used this test program on the Photon 2.

#include "Particle.h"

SYSTEM_MODE(AUTOMATIC);

#ifndef SYSTEM_VERSION_v620
SYSTEM_THREAD(ENABLED); // System thread defaults to on in 6.2.0 and later and this line is not required
#endif

SerialLogHandler logHandler(LOG_LEVEL_INFO);

const pin_t pwmPin = A4; // D1
const uint8_t toneValues[3] = {0, 50, 100};
const size_t toneCount = sizeof(toneValues) / sizeof(toneValues[0]);
size_t toneIndex = 0;
unsigned long toneSwitchLast = 0;
const std::chrono::milliseconds toneSwitchPeriod = 1s;

void setup() {
    pinMode(pwmPin, OUTPUT);
}

void loop() {
    if (millis() - toneSwitchLast >= toneSwitchPeriod.count()) {
        toneSwitchLast = millis();

        analogWrite(pwmPin, toneValues[toneIndex]);
        Log.info("tone %d", (int)toneValues[toneIndex]);

        if (++toneIndex >= toneCount) {
            toneIndex = 0;
        }
    }
}


This logic analyzer screenshot shows a 19% duty cycle which corresponds to the value of 50 (out of 255). The 0 and 100 values were also correct.