Just got started and encountered an issue

Ok, so I’m new to PlatformIO. I was getting very excited, started building out blink programs for all the various microcontrollers I have. Everything was going well. Then I started to migrate a project I am currently working on and it is encountering different behavior when compiled and pushed using PlatformIO vs Arduino IDE. It is a relatively simple light sequencer that uses the Adafruit NeoPixel library. I confirmed the versions of the library were the same between the Arduino sketch and the PlatformIO code. Arduino sketch works fine, the PlatformIO code doesn’t light up all the NeoPixels seems to only light up the first 10 or so. Then the remainder display as white. The code is identical between the two. I copied and pasted it from Arduino into PlatformIO then moved the loop() to the bottom. No other changes. This has me questioning PlatformIO which I really like from all other perspectives. But if I can’t trust the code copied in from Arduino IDE I’m not sure how to have confidence in PlatformIO.

#include <Adafruit_NeoPixel.h>

// Which pin on the Arduino is connected to the NeoPixels?
// On a Trinket or Gemma we suggest changing this to 1:
#define LED_PIN 6

// How many NeoPixels are attached to the Arduino?
#define LED_COUNT 21

// NeoPixel brightness, 0 (min) to 255 (max)
#define BRIGHTNESS 255  // Set BRIGHTNESS to about 1/5 (max = 255)

// Declare our NeoPixel strip object:
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB);
// Argument 1 = Number of pixels in NeoPixel strip
// Argument 2 = Arduino pin number (most are valid)
// Argument 3 = Pixel type flags, add together as needed:
//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
//   NEO_RGBW    Pixels are wired for RGBW bitstream (NeoPixel RGBW products)

void setup() {
  // These lines are specifically to support the Adafruit Trinket 5V 16 MHz.
  // Any other board, you can remove this part (but no harm leaving it):
#if defined(__AVR_ATtiny85__) && (F_CPU == 16000000)
  clock_prescale_set(clock_div_1);
#endif
  strip.begin();  // INITIALIZE NeoPixel strip object (REQUIRED)
  strip.show();   // Turn OFF all pixels ASAP
  strip.setBrightness(BRIGHTNESS);
}

void loop() {
  strip.clear();
  for (int i = 0; i < strip.numPixels(); i++) {
    strip.setPixelColor(i, strip.Color(255, 255, 255));
  }
  delay(500);
  strip.show();
  twoColorWipe(strip.Color(255, 0, 0), strip.Color(0, 255, 0), 100);
  whiteOverRainbow(50, 3);
  rainbowFade2White(50, 3, 1);
  colorWipe(strip.Color(255, 0, 0), 100);
  colorWipe(strip.Color(0, 255, 0), 50);
  colorWipe(strip.Color(0, 0, 255), 50);
  colorWipe(strip.Color(255, 0, 0), 250);
}

// Fill strip pixels one after another with a color. Strip is NOT cleared
// first; anything there will be covered pixel by pixel. Pass in color
// (as a single 'packed' 32-bit value, which you can get by calling
// strip.Color(red, green, blue) as shown in the loop() function above),
// and a delay time (in milliseconds) between pixels.
void colorWipe(uint32_t color, int wait) {
  for (int i = 0; i < strip.numPixels(); i++) {  // For each pixel in strip...
    strip.setPixelColor(i, color);               //  Set pixel's color (in RAM)
    strip.show();                                //  Update strip to match
    delay(wait);                                 //  Pause for a moment
  }
}

void twoColorWipe(uint32_t color, uint32_t color2, int wait) {
  for (int i = 0; i < strip.numPixels(); i++) {  // For each pixel in strip...
    if ((i % 2) == 0) {
      strip.setPixelColor(i, color);  //  Set pixel's color (in RAM)
      strip.show();                   //  Update strip to match
      delay(wait);                    //  Pause for a moment
      strip.clear();
      delay(wait);
    } else {
      strip.setPixelColor(i, color2);  //  Set pixel's color (in RAM)
      strip.show();                    //  Update strip to match
      delay(wait);                     //  Pause for a moment
      strip.clear();
      delay(wait);
    }
  }
  for (int i = strip.numPixels(); i >= 0; i--) {  // For each pixel in strip...
    if ((i % 2) == 0) {
      strip.setPixelColor(i, color);  //  Set pixel's color (in RAM)
      strip.show();                   //  Update strip to match
      delay(wait);                    //  Pause for a moment
    } else {
      strip.setPixelColor(i, color2);  //  Set pixel's color (in RAM)
      strip.show();                    //  Update strip to match
      delay(wait);                     //  Pause for a moment
    }
  }
  strip.clear();
  for (int i = 0; i < strip.numPixels(); i++) {  // For each pixel in strip...
    if ((i % 2) == 0) {
      strip.setPixelColor(i, color);  //  Set pixel's color (in RAM)
      strip.show();                   //  Update strip to match
      delay(wait);                    //  Pause for a moment
    } else {
      strip.setPixelColor(i, color2);  //  Set pixel's color (in RAM)
      strip.show();                    //  Update strip to match
      delay(wait);                     //  Pause for a moment
    }
  }
  delay(wait * 10);
}


void whiteOverRainbow(int whiteSpeed, int whiteLength) {

  if (whiteLength >= strip.numPixels()) whiteLength = strip.numPixels() - 1;

  int head = whiteLength - 1;
  int tail = 0;
  int loops = 3;
  int loopNum = 0;
  uint32_t lastTime = millis();
  uint32_t firstPixelHue = 0;

  for (;;) {                                       // Repeat forever (or until a 'break' or 'return')
    for (int i = 0; i < strip.numPixels(); i++) {  // For each pixel in strip...
      if (((i >= tail) && (i <= head)) ||          //  If between head & tail...
          ((tail > head) && ((i >= tail) || (i <= head)))) {
        strip.setPixelColor(i, strip.Color(0, 0, 0, 255));  // Set white
      } else {                                              // else set rainbow
        int pixelHue = firstPixelHue + (i * 65536L / strip.numPixels());
        strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(pixelHue)));
      }
    }

    strip.show();  // Update strip with new contents
    // There's no delay here, it just runs full-tilt until the timer and
    // counter combination below runs out.

    firstPixelHue += 40;  // Advance just a little along the color wheel

    if ((millis() - lastTime) > whiteSpeed) {  // Time to update head/tail?
      if (++head >= strip.numPixels()) {       // Advance head, wrap around
        head = 0;
        if (++loopNum >= loops) return;
      }
      if (++tail >= strip.numPixels()) {  // Advance tail, wrap around
        tail = 0;
      }
      lastTime = millis();  // Save time of last movement
    }
  }
}

void pulseWhite(uint8_t wait) {
  for (int j = 0; j < 256; j++) {  // Ramp up from 0 to 255
    // Fill entire strip with white at gamma-corrected brightness level 'j':
    strip.fill(strip.Color(0, 0, 0, strip.gamma8(j)));
    strip.show();
    delay(wait);
  }

  for (int j = 255; j >= 0; j--) {  // Ramp down from 255 to 0
    strip.fill(strip.Color(0, 0, 0, strip.gamma8(j)));
    strip.show();
    delay(wait);
  }
}

void rainbowFade2White(int wait, int rainbowLoops, int whiteLoops) {
  int fadeVal = 0, fadeMax = 100;

  // Hue of first pixel runs 'rainbowLoops' complete loops through the color
  // wheel. Color wheel has a range of 65536 but it's OK if we roll over, so
  // just count from 0 to rainbowLoops*65536, using steps of 256 so we
  // advance around the wheel at a decent clip.
  for (uint32_t firstPixelHue = 0; firstPixelHue < rainbowLoops * 65536;
       firstPixelHue += 256) {

    for (int i = 0; i < strip.numPixels(); i++) {  // For each pixel in strip...

      // Offset pixel hue by an amount to make one full revolution of the
      // color wheel (range of 65536) along the length of the strip
      // (strip.numPixels() steps):
      uint32_t pixelHue = firstPixelHue + (i * 65536L / strip.numPixels());

      // strip.ColorHSV() can take 1 or 3 arguments: a hue (0 to 65535) or
      // optionally add saturation and value (brightness) (each 0 to 255).
      // Here we're using just the three-argument variant, though the
      // second value (saturation) is a constant 255.
      strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(pixelHue, 255,
                                                          255 * fadeVal / fadeMax)));
    }

    strip.show();
    delay(wait);

    if (firstPixelHue < 65536) {                                 // First loop,
      if (fadeVal < fadeMax) fadeVal++;                          // fade in
    } else if (firstPixelHue >= ((rainbowLoops - 1) * 65536)) {  // Last loop,
      if (fadeVal > 0) fadeVal--;                                // fade out
    } else {
      fadeVal = fadeMax;  // Interim loop, make sure fade is at max
    }
  }

  for (int k = 0; k < whiteLoops; k++) {
    for (int j = 0; j < 256; j++) {  // Ramp up 0 to 255
      // Fill entire strip with white at gamma-corrected brightness level 'j':
      strip.fill(strip.Color(0, 0, 0, strip.gamma8(j)));
      strip.show();
    }
    delay(1000);                      // Pause 1 second
    for (int j = 255; j >= 0; j--) {  // Ramp down 255 to 0
      strip.fill(strip.Color(0, 0, 0, strip.gamma8(j)));
      strip.show();
    }
  }

  delay(500);  // Pause 1/2 second
}

Oh the board being used is MH-Tiny Attiny88

Ok, this gets even stranger. If I double the number for LED_COUNT it seems to work or at least mostly work. Which doesn’t make sense given that this is supposed to be the number of neopixels in a strand and Arduino is working with the exact number in the strand. If it is the same library, why the different results? I really like PlatformIO from a tool perspective just not sure how to debug situations like this where Arduino functions as expected and odd behavior from PlatformIO.

What platformio.ini did you use for this board?

What Arduino core did you install in the Arduino IDE for this board? GitHub - SpenceKonde/ATTinyCore: Arduino core for ATtiny 1634, 828, x313, x4, x41, x5, x61, x7 and x8 or some other one? PlatformIO lags behind in version of this one, so the version you have installed in the Arduino IDE matters there (latest in PIO: 1.5.2, Arduino IDE: 2.0.0-dev [unstable]).

The platformio.ini is this


[env:attiny88]
platform = atmelavr
board = attiny88
framework = arduino
upload_protocol = micronucleus
lib_deps = adafruit/Adafruit NeoPixel@^1.10.7

And the Arduino board manager is using this
https://raw.githubusercontent.com/MHEtLive/arduino-boards-index/master/package_mhetlive_index.json

But PlatformIO isn’t using this Arduino core at all, it uses the one linked above (ATTinyCore by SpenceKonde).

So the core basis is an entirely different one and can’t be easily compared.

You can still give it a try to a more up-to-date core by adding

platform_packages =
   maxgerhardt/framework-arduino-avr-attiny@2.0.0-dev

to your platformio.ini.

There’s also a PR open to get mhetlive88 support at Add support for MHETtiny88 board by lxxxxl · Pull Request #262 · platformio/platform-atmelavr · GitHub, so you would have to use the platformio.ini

[env:MHETtiny88]
platform = https://github.com/lxxxxl/platform-atmelavr.git
framework = arduino
board = MHETtiny88

you ofc need to have Git installed for this.

Thanks for the help, I’ll give these a try. I do have Git installed in fact it is one of the reasons I like PlatformIO so much I don’t have to keep switching to VSCode to do my commits, pushes, and pulls. I really do like the tool. I’m working through sandboxing all of my various micro controllers and common frameworks. If I can get them all working I’ll be using PlatformIO instead of Arduino for sure. Thanks again!

So I just tried another simple example with the RP2040 Nano Connect again copied code from Arduino and it didn’t work. Now I have been able to get code to work. But I’m wondering is it not an accurate expectation that Arduino sketches will compile and work? This was the Adafruit SSD1306 example code. I was troubleshooting and issue with icons displaying correctly. They work again in Arduino but not in platformio. They display but not correctly dimensions are off. So is platformio just a whole new learning curve?

With the same Arduino core (+version), libraries (+version) code and configuration (possible Tools->… configuration), you should get the same result.

Are you using ArduinoCore-mbed for the RP2040 Nano Connect in the Arduino IDE? If yes, of what version? What version are the relevant Adafruit (GFX, SSD1306) libraries? What is the the exact used sketch code?

Thanks for the reply. I understand what you are saying and I checked the library versions are in fact different. However, the point that I’m trying to understand is broader than that. So far every example I’ve tried other than “Blink” is slightly different. No matter which board, library combo I’m working with. Even the examples in the PlatformIO library manager are not setup correctly. They will have functions called during setup or loop that are defined after the loop. Now I understand that this is an easy fix, simply move those above the setup or loop. What I’m trying to understand is does PlatformIO simply require a completely new learning curve?

I just tried another SSD1306 library the ACROBOTIC example. Sure enough required modifications from the Arduino example to get it to work. Getting them to work initially I can find my way through. However, so far this seems to only get me started. Then once started there are other differences as well. I do completely understand that they are different compilers and other aspects. I’m not saying anything bad about the tool. It is awesome that it has been developed to this point. I’m just trying to clarify whether a new learning curve is required. Because so far I’ve used an ESP32, ATTiny85, ATTiny88, and RP2040. These have been used with various combinations of SSD1306 and NeoPixel libraries. I’ve been able to get “most” things to function, but I have NOT been able to simply copy and paste the Arduino example into PlatformIO and rearrange method calls.

This leads me to believe that once I get into more “complicated” troubleshooting it will require PlatformIO specific troubleshooting. Is that an accurate statement? If not, why do libraries such as the ACROBOTIC require changes to them in order to compile? What is it that Arduino IDE is doing behind the scenes that allow these examples to compile and execute without issue? As a developer do I need to understand that level of detail in order to effectively use PlatformIO? Again, not discounting the amazing work that has been done. Simply trying to decide whether or not I can use it currently given my level of experience and understanding. I wasn’t looking to dig deep into libraries yet. I’m still at the early stages of learning and trying to build a foundation of knowledge.

Thanks again for your help!

Ok, it has been a bit of a learning curve so I think I’ve answered my own question about that. However, I seem to have figured out using the Arduino IDE with versions of libraries that match those available in PlatformIO as closely as possible. Doing this and using the examples provided with the Arduino IDE seems to be a combination that is working well.

I’ve now been able to get OLED’s, SD Cards, WiFi, and others working with multiple boards in PlatformIO and I do really like the features. Thank you for all the help, to others that may be getting started be aware there are some differences and there is a bit of a learning curve as compared to the Arduino IDE, but worth it!

1 Like