TransWikia.com

Sprintf() function bricked Arduino Nano?

Electrical Engineering Asked by Dragos Spiridon on December 18, 2020

I am using an Arduino Nano clone to program and use a small I2C, SH1106 OLED.

While trying to create a function to give me the length of a char* (as all the solutions I found were for strings, and it would complain if they were replaced by char*,) I wanted to convert the integer I got back to char* (due to how the library I am using works) and as such, I used sprintf. Everything was working just fine until I added sprintf, now it looks like my board is bricked, as I cannot upload anything anymore to it.

Function which broke everything(which was part of the code block below):

char* len(char* msg){
  int c=0;
  char* length;
  while(msg[c] != '.'){
    c++;
  }
  sprintf(length, "%d", c);
  return length;
}

Code I’m trying to upload:

#include <OneBitDisplay.h>
#ifdef USE_BACKBUFFER
static uint8_t ucBackBuffer[1024];
#else
static uint8_t *ucBackBuffer = NULL;
#endif

#define SDA_PIN 32
#define SCL_PIN 26
#define RESET_PIN -1
#define OLED_ADDR -1
#define FLIP180 0
#define INVERT 0
#define USE_HW_I2C 1

#define MY_OLED OLED_128x64
#define OLED_WIDTH 128
#define OLED_HEIGHT 64

OBDISP obd;

void setup() {
int rc;
rc = obdI2CInit(&obd, MY_OLED, OLED_ADDR, FLIP180, INVERT, USE_HW_I2C, SDA_PIN, SCL_PIN, RESET_PIN, 800000L); // use standard I2C bus at 400Khz
}

void loop() {
  char* msg = "This is a test to see if i can manage to scroll text vertically on this oled screen right here. I will write some more words here so that the text will go off screen, as the small font is really smaller than I originally expected.";
 
  scroll_func(msg);
 
  delay(1000);

}

void scroll_func(char* msg){
  obdSetTextWrap(&obd, 1);
  int start=3;
  for(int i=0; i<1380; i+=126){
    if(start>=0){i=0;}
    obdFill(&obd, 0x0, 1);
    obdWriteString(&obd, i,0,start,(char *)msg, FONT_SMALL, 0, 1);
    if(start>=0){start--;}
    delay(1000);
  }
}

Error code received when trying to upload:

Arduino: 1.8.12 (Windows 10), Board: "Arduino Nano, ATmega328P (Old Bootloader)"

C:UsersDRAGOS~1AppDataLocalTemparduino_modified_sketch_983438scroll_func.ino: In function 'void loop()':

C:UsersDRAGOS~1AppDataLocalTemparduino_modified_sketch_983438scroll_func.ino:28:15: warning: ISO C++ forbids converting a string constant to 'char*' [-Wwrite-strings]

   char* msg = "This is a test to see if i can manage to scroll text vertically on this oled screen right here. I will write some more words here so that the text will go off screen, as the small font is really smaller than I originally expected.";

               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Sketch uses 7894 bytes (25%) of program storage space. Maximum is 30720 bytes.
Global variables use 826 bytes (40%) of dynamic memory, leaving 1222 bytes for local variables. Maximum is 2048 bytes.
C:UsersDragos SPiridonAppDataLocalArduino15packagesarduinotoolsavrdude6.3.0-arduino17/bin/avrdude -CC:UsersDragos SPiridonAppDataLocalArduino15packagesarduinotoolsavrdude6.3.0-arduino17/etc/avrdude.conf -v -patmega328p -carduino -PCOM6 -b57600 -D -Uflash:w:C:UsersDRAGOS~1AppDataLocalTemparduino_build_269143/scroll_func.ino.hex:i

avrdude: Version 6.3-20190619
         Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/
         Copyright (c) 2007-2014 Joerg Wunsch

         System wide configuration file is "C:UsersDragos SPiridonAppDataLocalArduino15packagesarduinotoolsavrdude6.3.0-arduino17/etc/avrdude.conf"

         Using Port                    : COM6
         Using Programmer              : arduino
         Overriding Baud Rate          : 57600
         AVR Part                      : ATmega328P
         Chip Erase delay              : 9000 us
         PAGEL                         : PD7
         BS2                           : PC2
         RESET disposition             : dedicated
         RETRY pulse                   : SCK
         serial program mode           : yes
         parallel program mode         : yes
         Timeout                       : 200
         StabDelay                     : 100
         CmdexeDelay                   : 25
         SyncLoops                     : 32
         ByteDelay                     : 0
         PollIndex                     : 3
         PollValue                     : 0x53
         Memory Detail                 :

                                  Block Poll               Page                       Polled
           Memory Type Mode Delay Size  Indx Paged  Size   Size #Pages MinW  MaxW   ReadBack
           ----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
           eeprom        65    20     4    0 no       1024    4      0  3600  3600 0xff 0xff
           flash         65     6   128    0 yes     32768  128    256  4500  4500 0xff 0xff
           lfuse          0     0     0    0 no          1    0      0  4500  4500 0x00 0x00
           hfuse          0     0     0    0 no          1    0      0  4500  4500 0x00 0x00
           efuse          0     0     0    0 no          1    0      0  4500  4500 0x00 0x00
           lock           0     0     0    0 no          1    0      0  4500  4500 0x00 0x00
           calibration    0     0     0    0 no          1    0      0     0     0 0x00 0x00
           signature      0     0     0    0 no          3    0      0     0     0 0x00 0x00

         Programmer Type : Arduino
         Description     : Arduino
         Hardware Version: 2
         Firmware Version: 1.16
         Vtarget         : 0.0 V
         Varef           : 0.0 V
         Oscillator      : Off
         SCK period      : 0.1 us

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.00s

avrdude: Device signature = 0x1e950f (probably m328p)
avrdude: reading input file "C:UsersDRAGOS~1AppDataLocalTemparduino_build_269143/scroll_func.ino.hex"
avrdude: writing flash (7894 bytes):

Writing | avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_recv(): programmer is not responding
Problem uploading to board.  See http://www.arduino.cc/en/Guide/Troubleshooting#upload for suggestions.

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.

From what I can deduce, the board cannot be written to anymore. It is still detected in device manager(Windows) as CH340 device, and "Get board info" in the IDE still works, but nothing uploads to it.

And yes, I have tried all of the first options such as switching COM ports from USB3 to 2, it is running the old bootloader.

2 Answers

Remember that a char* is a pointer (like a reference) to another object. When calling sprintf, the first argument is a char* which points to a buffer where the sprintf output will be stored. The C language assumes that you, the programmer, are responsible for making sure that buffer exists. Your code violates that rule by passing an uninitialized pointer to sprintf:

char* length; // bad. missing the storage to receive sprintf output.
sprintf(length, "%d", 1234); // crash. length isn't pointing to a buffer object.

This code crashes because the "length" buffer given to sprintf is not valid.

Instead, you need to provide the storage space where the sprintf string output will be stored.

Unfortunately, you've wandered into one of the dark corners of C programming: when trying to return a string from a function, who owns the memory where the string is stored? There are three ways to deal with returning a C string from a function:

  • static buffer
  • dynamic buffer which must be destroyed by caller
  • caller-allocated buffer

One approach is to use a static buffer allocated inside the function:

static char length[8]; // equivalent to char* length = "new writeable buffer of 8 characters"
sprintf(length, "%d", 1234); // good

This is a simple, straightforward implementation. Drawback with this approach is that the value will change every time the function is called. The function does not return independent values each time it is called, it returns the location where the one-and-only return string lives. I can't say whether that is ok for your design or not.

Another approach is to use dynamic allocation, which is more complicated. The function would have to allocate a new buffer (using either new() or malloc()). Advantage is that each function call returns a distinct, independent value. Disadvantage is that the function caller owns the buffer, and is responsible for returning the buffer after it is no longer needed. Failing to return the memory (using delete() or free()) will cause the program to eventually crash due to running out of memory.

More at https://arduino.stackexchange.com/questions/42986/convert-int-to-char

Answered by MarkU on December 18, 2020

Your len() function is writing to unreserved memory (a buffer overrun). It's also looking for a '.' to terminate the string, the first of which is half-way through your msg.

Instead, you could use the libc strlen() function which will look for the NULL terminator at the end of msg, something like this:

void loop()
{
  char msg[] = "This is a test to see if i can manage to scroll text vertically on this OLED screen right here. I will write some more words here so that the text will go off screen, as the small font is really smaller than I originally expected.";

  // Get the length of the message.
  int length = strlen(msg);

  // Calculate the number of decimal digits in length.
  int length_of_length = ceil(log10(length + 1));

  // Create a string that is long enough to hold the decimal digits and the NULL terminator.
  char length_str[length_of_length + 1];

  // Write the decimal digits to the string. sprintf automatically adds a NULL terminator.
  sprintf(length_str, "%d", length);
}

Answered by tim on December 18, 2020

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP