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:\Users\DRAGOS~1\AppData\Local\Temp\arduino_modified_sketch_983438\scroll_func.ino: In function 'void loop()':
C:\Users\DRAGOS~1\AppData\Local\Temp\arduino_modified_sketch_983438\scroll_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:\Users\Dragos SPiridon\AppData\Local\Arduino15\packages\arduino\tools\avrdude6円.3.0-arduino17/bin/avrdude -CC:\Users\Dragos SPiridon\AppData\Local\Arduino15\packages\arduino\tools\avrdude6円.3.0-arduino17/etc/avrdude.conf -v -patmega328p -carduino -PCOM6 -b57600 -D -Uflash:w:C:\Users\DRAGOS~1\AppData\Local\Temp\arduino_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:\Users\Dragos SPiridon\AppData\Local\Arduino15\packages\arduino\tools\avrdude6円.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:\Users\DRAGOS~1\AppData\Local\Temp\arduino_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.
-
\$\begingroup\$ Is this report from the first time you tried to upload code with problematic function? As your report shows error from your programmer not your board. If yes then your board dont even have "problematic" code in it. \$\endgroup\$Rokta– Rokta2020年07月21日 12:30:28 +00:00Commented Jul 21, 2020 at 12:30
-
\$\begingroup\$ @Rokta the programmer is the board \$\endgroup\$Stack Exchange Broke The Law– Stack Exchange Broke The Law2020年07月21日 14:18:25 +00:00Commented Jul 21, 2020 at 14:18
-
\$\begingroup\$ @Rokta I have uploaded the code to the board before, but the problematic function did not contain the "char* length" and sprintf() lines, it would just return c. Once i have added the two lines and uploaded (a successful upload, yes), the oled was not doing anything and then i removed them and tried to re-upload, to no avail... \$\endgroup\$Dragos Spiridon– Dragos Spiridon2020年07月21日 17:06:47 +00:00Commented Jul 21, 2020 at 17:06
-
1\$\begingroup\$ the upload problem is in no way related to the code. the avrdude resets the board and the upload is handled by the bootloader. your code is not running at this time. check the cable and connections. do you have something wired to pins 0 and 1? \$\endgroup\$Juraj– Juraj2020年07月22日 11:29:54 +00:00Commented Jul 22, 2020 at 11:29
-
\$\begingroup\$ @Juraj no, I do not have anything connected to it, all I have are 4 wires connecting the VCC, GND, SDA and SCL of the OLED to the appropriate ports of the board. MarkU gave me some advice in the answer below but it did not seem to help either \$\endgroup\$Dragos Spiridon– Dragos Spiridon2020年07月22日 13:21:53 +00:00Commented Jul 22, 2020 at 13:21
3 Answers 3
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
-
\$\begingroup\$ Thank you for the insight, but in the position i'm in, where due to this dumb mistake of mine now I cannot write to the board anymore, what does thus mean? Is it irreversibly broken? Can I do something to remedy this? \$\endgroup\$Dragos Spiridon– Dragos Spiridon2020年07月21日 21:09:04 +00:00Commented Jul 21, 2020 at 21:09
-
\$\begingroup\$ Ah -- so the actual problem is that you can't load new firmware into the board? Try this: when you run avrdude to load the firmware, hold down the reset button until you see "avrdude: stk500_recv(): programmer is not responding", then release reset. Sometimes avrdude gets confused if the firmware is printing output when avrdude is expecting to see the bootloader. Pressing and releasing reset puts the board into bootloader mode for about 2 seconds before the user firmware starts talking. \$\endgroup\$MarkU– MarkU2020年07月21日 22:08:23 +00:00Commented Jul 21, 2020 at 22:08
-
\$\begingroup\$ Indeed, that is my problem. I have tried your advice, once it gets to the writing part, and released it after first "avrdude: stk500_recv(): programmer is not responding", but it did not seem to help, instead, the on-board LED and the RX LED were on (not flashing, just on). \$\endgroup\$Dragos Spiridon– Dragos Spiridon2020年07月22日 13:05:18 +00:00Commented Jul 22, 2020 at 13:05
I don't think it is possible that your code could brick the board for the following reasons:
During firmware upload the bootloader takes control of the serial interface and the user firmware is not running, neither the old one (if present) neither the new one (the one being loaded).
The bootloader resides in a separate part of the flash that can be written only using parallel programming (i.e. bypassing the CH340G USB to USART converter and connecting a suitable programmer circuit directly to the MCU). In other words, you cannot upload a new bootloader using the serial interface and so no serial upload can mess-up the bootloader either.
The code present in the part of the flash that contains user code is separate from the part containing the bootloader. Moreover, no code in that "user code flash" can access the bootloader part. This means that your use of sprintf, even if your code is utterly buggy, cannot mess-up the bootloader.
To summarize, it is almost impossible that the simple act of uploading the firmware could directly brick the board. The only way to brick the board in a "software way" is to program the so-called fuse bits, in particular those that allow disabling further programming (it's a security measure on many MCUs). However fuse bits can be altered only using parallel programming, so not your case.
In theory buggy firmware could thrash some peripheral hardware register and configure the MCU in some weird mode that can cause a hardware fault, but that is really unlikely, UNLESS you have external circuitry connected to the board that was driven in an unexpected way and damaged the board (for example causing an overload on some GPIO pin).
So I suggest you to disconnect everything from your board (even ground wires) and just try to upload a simple firmware (classic "blinky", for example) and see if it works. Before the upload also disconnect the board from the USB cable and let it "cool down" for 30sec or so before reconnecting it to the PC (to allow the USB bus to "clean up")
If that doesn't work, you could have indeed an hardware fault, but without seeing your setup and your circuits it is impossible to make an educated guess on what really happened.
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);
}
-
\$\begingroup\$ Two things: strlen() is a libc function. Second: You also have a buffer overflow (off-by-one) in your code, length_of_length doesn't accomodate for the 0 byte that is appended. \$\endgroup\$Felix S– Felix S2020年07月21日 12:25:27 +00:00Commented Jul 21, 2020 at 12:25
-
\$\begingroup\$ Thanks. I've edited now. \$\endgroup\$tim– tim2020年07月21日 12:52:56 +00:00Commented Jul 21, 2020 at 12:52
-
\$\begingroup\$ That's a very nice way of doing it, and i thank you for the idea, but my concern is whether or not that buffer overrun could have bricked the microprocessor? Is the buffer overrun what is causing my inability to write to the board anymore? Is there any way to remedy that? \$\endgroup\$Dragos Spiridon– Dragos Spiridon2020年07月21日 17:00:27 +00:00Commented Jul 21, 2020 at 17:00
-
1\$\begingroup\$ Daft question, but have you tried switching it off and on again? I can't see how overwriting RAM with bad data would trash the chip. My original erroneous answer, before I edited it thanks to @felix-s, simply overwrote
length
with0
. \$\endgroup\$tim– tim2020年07月22日 22:28:48 +00:00Commented Jul 22, 2020 at 22:28