How can I make this Arduino script work?

This is where moving from Arduino to PIO breaks down.
I have a script that should work in Arduino, but nothing is declared correctly for the C++ compiler.

#include <Wire.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_GFX.h>
#define OLED_ADDR   0x3C
Adafruit_SSD1306 display(-1);
#if (SSD1306_LCDHEIGHT != 64)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif
#if (SSD1306_LCDHEIGHT != 64)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif
#define encoder0PinA  2
#define encoder0PinB  3
volatile unsigned int encoder0Pos = 0;
int valA;
int valB;
int valC;
byte clk;
byte menuCount = 1;
byte dir = 0;
bool runState = false;
void setup() {
  pinMode(6, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  Serial.begin(9600);
  display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
  display.display();
  display.clearDisplay();
  attachInterrupt(0, doEncoder, CHANGE);  // encoder pin on interrupt 0 - pin 2
}

void loop() {
  clk = digitalRead(4);
  ledControl();
  menuCheck();
  staticMenu();
  display.clearDisplay();
  delay(50);
}

void staticMenu() {
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(10, 0);
  display.println("yyRobotic");
  //---------------------------------
  display.setTextSize(1);
  display.setCursor(10, 20);
  display.println("Value A:");
  display.setCursor(60, 20);
  display.println(valA);
  display.setCursor(10, 30);
  display.println("Value B:");
  display.setCursor(60, 30);
  display.println(valB);
  display.setCursor(10, 40);
  display.println("Value C:");
  display.setCursor(60, 40);
  display.println(valC);
  display.setCursor(10, 50);
  display.println("Start:");
  display.setCursor(45, 50);
  if (encoder0Pos > 5 && menuCount == 4) {
    display.println("Run!");
    runState = true;
  } else {
    display.println("Idle");
    runState = false;
  }
  display.setCursor(2, (menuCount * 10) + 10);
  display.println(">");
  display.display();
}

void ledControl() {
  if (runState == true) {
    analogWrite(6, valA);
    analogWrite(9, valB);
    analogWrite(10, valC);
  }
}

void menuCheck() {
  if (clk == LOW && menuCount < 5) {
    menuCount++;
    encoder0Pos = 0;
  }

  if (clk == LOW && menuCount >= 5) {
    menuCount = 1;
  }

  if (menuCount == 1) {
    valA = encoder0Pos;
  }
  if (menuCount == 2) {
    valB = encoder0Pos;
  }
  if (menuCount == 3) {
    valC = encoder0Pos;
  }
}

void doEncoder() {
  if (digitalRead(encoder0PinA) == HIGH) {
    if (digitalRead(encoder0PinB) == LOW && encoder0Pos > 0) {
      encoder0Pos = encoder0Pos - 1;
      dir = 0;
    }
    else {
      encoder0Pos = encoder0Pos + 1;
      dir = 1;
    }
  }
  else
  {
    if (digitalRead(encoder0PinB) == LOW ) {
      encoder0Pos = encoder0Pos + 1;
      dir = 1;
    }
    else {
      if (encoder0Pos > 0) {
        encoder0Pos = encoder0Pos - 1;
        dir = 0;
      }
    }
  }
}
*** [.pio\build\esp32dev\lib7ae\encoder\Encoder.cpp.o] Error 1
========================================================================================= [FAILED] Took 3.68 seconds =========================================================================================
The terminal process "C:\Users\joema\.platformio\penv\Scripts\platformio.exe 'run'" terminated with exit code: 1.

Terminal will be reused by tasks, press any key to close it.

> Executing task in folder Rotary_OLED_R32: C:\Users\joema\.platformio\penv\Scripts\platformio.exe run <

Processing esp32dev (platform: espressif32; board: esp32dev; framework: arduino)
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Verbose mode can be enabled via `-v, --verbose` option
CONFIGURATION: https://docs.platformio.org/page/boards/espressif32/esp32dev.html
PLATFORM: Espressif 32 1.12.4 > Espressif ESP32 Dev Module
HARDWARE: ESP32 240MHz, 320KB RAM, 4MB Flash
DEBUG: Current (esp-prog) External (esp-prog, iot-bus-jtag, jlink, minimodule, olimex-arm-usb-ocd, olimex-arm-usb-ocd-h, olimex-arm-usb-tiny-h, olimex-jtag-tiny, tumpa)
PACKAGES:
 - framework-arduinoespressif32 3.10004.200129 (1.0.4)
 - tool-esptoolpy 1.20600.0 (2.6.0)
 - toolchain-xtensa32 2.50200.80 (5.2.0)
LDF: Library Dependency Finder -> http://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 43 compatible libraries
Scanning dependencies...
Dependency Graph
|-- <Adafruit GFX Library> 1.9.0
|   |-- <Adafruit BusIO> 1.4.1
|   |   |-- <Wire> 1.0.1
|   |   |-- <SPI> 1.0
|   |-- <SPI> 1.0
|   |-- <Wire> 1.0.1
|-- <Adafruit SSD1306> 2.3.1
|   |-- <Adafruit GFX Library> 1.9.0
|   |   |-- <Adafruit BusIO> 1.4.1
|   |   |   |-- <Wire> 1.0.1
|   |   |   |-- <SPI> 1.0
|   |   |-- <SPI> 1.0
|   |   |-- <Wire> 1.0.1
|   |-- <SPI> 1.0
|   |-- <Wire> 1.0.1
|-- <Wire> 1.0.1
Building in release mode
Compiling .pio\build\esp32dev\src\main.cpp.o
Archiving .pio\build\esp32dev\libdaf\libAdafruit GFX Library.a
src\main.cpp: In function 'void setup()':
src\main.cpp:37:22: error: 'doEncoder' was not declared in this scope
   attachInterrupt(0, doEncoder, CHANGE);  // encoder pin on interrupt 0 - pin 2
                      ^
src\main.cpp: In function 'void loop()':
src\main.cpp:42:14: error: 'ledControl' was not declared in this scope
   ledControl();
              ^
src\main.cpp:43:13: error: 'menuCheck' was not declared in this scope
   menuCheck();
             ^
src\main.cpp:44:14: error: 'staticMenu' was not declared in this scope
   staticMenu();
              ^
src\main.cpp: In function 'void ledControl()':
src\main.cpp:90:24: error: 'analogWrite' was not declared in this scope
     analogWrite(6, valA);
                        ^
Archiving .pio\build\esp32dev\libc20\libAdafruit SSD1306.a
Compiling .pio\build\esp32dev\FrameworkArduino\base64.cpp.o
*** [.pio\build\esp32dev\src\main.cpp.o] Error 1
========================================================================================= [FAILED] Took 4.08 seconds =========================================================================================
The terminal process "C:\Users\joema\.platformio\penv\Scripts\platformio.exe 'run'" terminated with exit code: 1.

Terminal will be reused by tasks, press any key to close it.

> Executing task in folder Rotary_OLED_R32: C:\Users\joema\.platformio\penv\Scripts\platformio.exe run <

Processing esp32dev (platform: espressif32; board: esp32dev; framework: arduino)
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Verbose mode can be enabled via `-v, --verbose` option
CONFIGURATION: https://docs.platformio.org/page/boards/espressif32/esp32dev.html
PLATFORM: Espressif 32 1.12.4 > Espressif ESP32 Dev Module
HARDWARE: ESP32 240MHz, 320KB RAM, 4MB Flash
DEBUG: Current (esp-prog) External (esp-prog, iot-bus-jtag, jlink, minimodule, olimex-arm-usb-ocd, olimex-arm-usb-ocd-h, olimex-arm-usb-tiny-h, olimex-jtag-tiny, tumpa)
PACKAGES:
 - framework-arduinoespressif32 3.10004.200129 (1.0.4)
 - tool-esptoolpy 1.20600.0 (2.6.0)
 - toolchain-xtensa32 2.50200.80 (5.2.0)
LDF: Library Dependency Finder -> http://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 43 compatible libraries
Scanning dependencies...
Dependency Graph
|-- <Adafruit GFX Library> 1.9.0
|   |-- <Adafruit BusIO> 1.4.1
|   |   |-- <Wire> 1.0.1
|   |   |-- <SPI> 1.0
|   |-- <SPI> 1.0
|   |-- <Wire> 1.0.1
|-- <Adafruit SSD1306> 2.3.1
|   |-- <Adafruit GFX Library> 1.9.0
|   |   |-- <Adafruit BusIO> 1.4.1
|   |   |   |-- <Wire> 1.0.1
|   |   |   |-- <SPI> 1.0
|   |   |-- <SPI> 1.0
|   |   |-- <Wire> 1.0.1
|   |-- <SPI> 1.0
|   |-- <Wire> 1.0.1
|-- <Wire> 1.0.1
Building in release mode
Compiling .pio\build\esp32dev\src\main.cpp.o
src\main.cpp: In function 'void setup()':
src\main.cpp:37:22: error: 'doEncoder' was not declared in this scope
   attachInterrupt(0, doEncoder, CHANGE);  // encoder pin on interrupt 0 - pin 2
                      ^
src\main.cpp: In function 'void loop()':
src\main.cpp:42:14: error: 'ledControl' was not declared in this scope
   ledControl();
              ^
src\main.cpp:43:13: error: 'menuCheck' was not declared in this scope
   menuCheck();
             ^
src\main.cpp:44:14: error: 'staticMenu' was not declared in this scope
   staticMenu();
              ^
src\main.cpp: In function 'void ledControl()':
src\main.cpp:90:24: error: 'analogWrite' was not declared in this scope
     analogWrite(6, valA);
                        ^
Compiling .pio\build\esp32dev\FrameworkArduino\cbuf.cpp.o
Compiling .pio\build\esp32dev\FrameworkArduino\esp32-hal-adc.c.o
*** [.pio\build\esp32dev\src\main.cpp.o] Error 1
Compiling .pio\build\esp32dev\FrameworkArduino\esp32-hal-bt.c.o
========================================================================================= [FAILED]

First things first, did you use PlatformIO’s ability to import an Arduino sketch? Or did you copy and paste into a file, main.cpp in your src folder?

You appear to be missing #include <Arduino.h>.

Does platformio.ini contain the line framework = arduino, or are you trying to use plain AVR C++ code instead?

When pasting code, or errors, please first type 3 back ticks (`) on a line of their own, paste the source code on the next line, then type another three backticks on a new line following the last of your pasted lines. Like this:

```
void setup() {
    Serial.begin(9600);
    ....
```

Reading other people’s code is what I do for a living, but without the backticks, formatting is lost and it makes life harder than it needs to be.

Thanks.

Cheers,
Norm.

2 Likes

:grumble mumble:

I have once again edited your posts to add the code formatting (as described by Norman). Please read his post on that point several times, and internalise it, this is getting pretty old having to keep repeating that you need to do it.

:grumble over:

Have a look at the order stuff is happening in the code. In setup(), there is reference to doEncoder() via the attachInterrupt() statement. Does the compiler know about the existence of this function yet (the answer you are looking for is a big fat no). And again, in the loop() function, there are then calls to ledControl(), menuCheck() and staticMenu(), which the compiler also doesn’t know about.

Think of this this way. Put yourself in the shoes of the compiler (as they are a very simple beast). Read the file from top to bottom. You see the includes, so you pull the content of them in. You see the defines, so you know to substitute those keywords if you see them further down. You see the variables defined, so you know they will be used somewhere the code. Then you process setup because it’s the first function you’ve seen so far in the file. You see all the references to pinMode, Serial, attachInterrupt, which all come from Arduino.h, so all is good (er, well, you DIDN’T #include <Arduino.h>, which you should have, but were saved by the fact one of the Adafruit libraries did, so it only luck you didn’t get errors about those functions). So all is good until you get to …

attachInterrupt(0, doEncoder, CHANGE);

… at which point you scratch your head and say “what the heck is doEncoder(), and what am I supposed to do now?”

Regardless, you keep reading through the file. You see loop() next. digitialRead() is fine - and we know about the clk variable from before. Er… ledControl()?? menuCheck()?? staticMenu()?? Don’t know anything about them. Rest of loop() is fine. So keep on going - this function, that function… too late though, you didn’t know about them when you needed to earlier. I can’t say why the analogWrite() error was thrown - it could just be a by-product of all the others.

How to fix the issue. Let the compiler know about the functions BEFORE you call them. Otherwise known as … function declarations. You are saying ‘I declare that this function will be defined later, but I’m telling you that it exists’. No different to all the variable declarations up the top.

In other words, putting the function declarations …

void doEncoder();
void ledControl();
void menuCheck();
void staticMenu();

… above void setup() / before any of the functions of your code will fix this problem.

Then when the compiler plods it’s way through the code, it will go, “oh, that’s right, you told me about this function… I’ll look for it later and keep going”, since it assumes that you, as the programmer, will in fact define the function at a later point. Otherwise, you will get the “function not defined” error at the end of the compile when everything is linked together.

This is basic C++ - have a look at this article if you need more help on this.

1 Like

What @pfeerick is explaining is how a normal C++ program would be written. The Arduino system hides a lot of the complexity from you, plus, it doesn’t require you to pre-declare functions before you use them in code.

Tutorial for creating multi cpp file arduino project - #36 by NormanDunbar is something I wrote which explains why this works in Arduino speak, but not in normal C++.

If you are learning C++ from scratch, then perhaps Arduino sketches are not the best starting point. You should be looking at some plain AVR C++ code instead, that is normal C++.

HTH

Cheers,
Norm.

1 Like

This may be tremendous help. First, thanks for the ‘how we do stuff’ about formatting. I don’t want to be an ass.

So it really is that simple: Just "void x();’ and it’s declared? That just seemed to easy.

Yes, I want to write proper C++, because I ultimately want to write a microchip and I don’t trust the secret help Arduino gives. And I want to share stuff that’s portable.

Arduino is gigantic mountain of material, and that’s where I was. So that’s where I am. I will find some of that AVR stuff. Seeing the differences will be more tremendous help.

Got it.
1 Like

I saw your paper a while ago, but it exceeded my capacity.

void ledControl() {

  if (runState == true) {

    analogWrite(6, valA);

    analogWrite(9, valB);

    analogWrite(10, valC);

  }

}

The first of three ‘analogWrite’ and the ‘valA’ are squiggled. Why?
Isn’t analogWrite standard?

I get that sometimes the compiler will give de novo errors caused by a confluence of errors. But it didn’t go away.

Yes, it’s that simple. However, if the function isn’t void then you would have to declare it correctly. For example:

int getPinState(byte pin) {
    ....
    return some_pin_state_value;
}

would have to be declared as:

int getPinState(byte pin);

Or:

int getPinState(byte);

Hopefully, you already know this and I’m not “teaching grandma to suck eggs”! :wink:

Cheers,
Norm.

2 Likes

Yup, as Norman said, it is that easy. Remember, I said compilers are a very simple beast… and easily confused! :wink: The only catch is the signature of the declaration needs to match that of the definition. But that’s why it’s as easy as copying and pasting it, and putting the semicolon on the end of the statement.

All you need do is either put the entire function definition before the first reference to it

ie.

#include <Arduino.h>

void sayHello()
{
   Serial.println("Hello!");
}

void setup()
{
   Serial.begin(9600);
}

void loop()
{
   sayHello();
   delay(1000);
}

or do the function declaration in at the top, then the definition can go anywhere - making it so you can order the code as you wish.

#include <Arduino.h>

void sayHello();

void setup()
{
   Serial.begin(9600);
}

void loop()
{
   sayHello();
   delay(1000);
}

void sayHello()
{
   Serial.println("Hello!");
}

Glad to hear it’s starting to make sense now :wink:

Have a look at the ‘Problems’ tab (View -> Problems if is currently hidden) - what does intellisense say it thinks is the problem? From a quick read through of the code - I don’t see valA, valB or valC ever actually set to a value - meaning it’s a random or indeterminate value. Normally you would initalise them to a known value when you declare them. e.g. int valA = 0; instead of int valA; as it is now. Or int valA = -1; if the value doesn’t go negative - then you will know it is still at the default value if it is -1.

Although having said that… I didn’t get anything when I tried that code on my system, so unless there is something else different, I dunno why IntelliSense is having a hissy fit.

:frowning_face:

I shall summarise.

At the moment, you are sorting out why compiling your *.cpp code is giving errors about undeclared functions.

When the Arduino system compiles an *.ino sketch, it looks for these undeclared functions, finds what they should be declared as, then adds the declarations to the top of your sketch, which it has copied off to a temporary working location.

That’s only a part of what it does for you in the background. There’s lots, lots, more!

Cheers,
Norm.

1 Like

There is an excellent book by Elliot Williams on programming the ATmega168 using plain AVR C++/C. Amazon (UK) has it at Amazon.co.uk.

It uses a breadboard based ATmega168 at 8MHz, but thats just a smaller version of the Arduino’s ATmega328 which runs at 16 MHz. Other than that, the two AVRs are identical.

There is a bug in the earlier chapters when he refers to the blink program “seen earlier” when it actually comes later! Other than that, it’s all good.

HTH

Cheers,
Norm.

2 Likes

I have a similar problem.

when I try to compile my sketch it says, ‘analogWrite was not declared in this scope’

Please do help me

Thanks

Remove <home dir>\.platformio\packages* and <home dir>\.platformio\.cache and rebuild.

What do you mean by ‘rebuild’

“Pressing the build button again”.

Actually, I’ve even tried reinstalling both VS Code and Platform IO.
And shows the same errors

Good news, after uninstalling platform io and installing it manually I could fix the issue