Hello Arduino community, the program I'm trying to make is:
My ESP fetches my school's timetable:
-to do that, it needs to:
--log-in via login endpoint, extract the access_token
--send another http request to the timetable endpoint and extract json that gets sent back
An eink display displays the timetable for the week
Unfortunately I haven't yet been able to get the access_token extracting part working.
Here's how I'm trying to do it:
- receive the http response with the token in the body in json format
- check if response code is 200
- skip all characters until the json opening '{'
- find the 'access_header' key and stop right before it
- load the key into buffer until i hit a " < this is the error part
Here's my complete code for context. I tried to put comments to make it easier to understand. The most important part is the last loop of the getHeader() function
#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>
#include <ArduinoJson.h>
#define TIMEOUT 10000
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(115200);
Serial.println();
Serial.println();
initWifi(); // basic wifi connecting function
JsonDocument timetable; // the json where ill eventually be saving my timetable, not important
if(getTimetable(timetable))
fail();
}
void loop() {
}
void initWifi() {
WiFi.mode(WIFI_STA);
WiFi.setPhyMode(WIFI_PHY_MODE_11G);
WiFi.begin(F("SSID"), F("PASSWORD"));
int dots = 0;
Serial.print(F("wifi "));
while (WiFi.status() != WL_CONNECTED) {
Serial.print(WiFi.status());
Serial.flush();
dots++;
if (dots > 100)
fail();
visualDelay(500);
yield();
}
Serial.println();
Serial.println(F("done"));
}
int getTimetable(JsonDocument& timetable) {
WiFiClientSecure client;
client.setInsecure();
client.setBufferSizes(512,512); // low buffer size to save memory, the server does support it
if (client.connect(F("my-school-server"), 443)) {
Serial.println(F("connected"));
} else {
Serial.println(F("fail"));
return -2;
}
client.println(F("POST /api/login HTTP/1.1"));
client.println(F("host: my-school-server"));
client.println(F("accept: */*"));
client.println(F("content-length: 73"));
client.println(F("content-type: application/x-www-form-urlencoded"));
client.println();
client.print(F("body-that-contains-login-info"));
// just a simple loop that checks if data has arrived yet
if(waitForData(client)) {
Serial.println(F("timeout"));
return -3;
}
// check for 200 and dont proceed otherwise
if(failed(client)) {
Serial.println(F("non 200 response code"));
return -4;
} else {
Serial.println(F("200"));
}
char header[6000];
getHeader(client, header);
client.abort();
return 0;
}
void getHeader(WiFiClientSecure& client, char (&header)[6000]) {
while (readw(client) != '{') // skip until the opening of the json file
;
size_t i;
int c;
const char* tokstr = "\"access_token\": \""; // the json key i am searching for
char buffer[20];
while ((c = readw(client)) != -1) {
for (i = 0; i < sizeof(buffer) - 2; i++)
buffer[i] = buffer[i + 1];
buffer[i++] = c;
buffer[i] = '\0';
if (strstr(buffer, tokstr)) // if found in the last 19 chars break
break;
yield();
}
Serial.println(ESP.getFreeHeap()); // prints over 20k
int x;
// now just to test i fill the buffer 2 times to show there is no error and its not a memory problem
for(x = 0; x < sizeof(header)-1; x++)
header[x] = 'n';
header[x] = '\0';
Serial.println(header);
for(x = 0; x < sizeof(header)-1; x++)
header[x] = 'x';
header[x] = '\0';
Serial.println(header);
// now i need to construct the header, i first put the authorization part of the header into the buffer and then start loading the stream into it until i hit a '"' char that means the end of the json string.
// THIS IS WHERE THE ERROR IS
const char* headerStart = "authorization: Bearer ";
strcpy(header, headerStart);
i = strlen(header);
while((c = readw(client)) != -1 && c != '"' && i < sizeof(header) - 1) {
header[i++] = c;
Serial.println(i);
yield(); //yield due to watchdog crashing
}
header[i] = '\0';
Serial.println(header);
}
int failed(WiFiClientSecure& client) {
char buffer[20];
const char* success = "200";
int i = client.readBytesUntil('\n', buffer, sizeof(buffer) - 1);
buffer[i] = '\0';
if (!strstr(buffer, success)) {
return 1;
} else {
return 0;
}
}
int waitForData(WiFiClientSecure& client) {
unsigned long t0 = millis();
while (client.available() == 0) {
if (millis() - t0 > TIMEOUT) {
return 1;
}
yield();
}
return 0;
}
// read that tries twice in case some data hasnt arrived yet, i found its important due to the small buffer size
int readw(WiFiClientSecure& client) {
int c;
if((c = client.read()) != -1)
return c;
Serial.println(F("no data tryin again"));
delay(100);
yield();
if((c = client.read()) != -1)
return c;
return -1;
}
void visualDelay(unsigned long duration) { // delay that also flashes the led
digitalWrite(LED_BUILTIN, LOW);
delay(duration / 2);
digitalWrite(LED_BUILTIN, HIGH);
delay(duration / 2);
}
void fail() { // in case of error, just start flashing the led
Serial.println(F("failed!"));
for (;;) {
visualDelay(3000);
}
}
Here's the exception:
20648 // over 20k of free heap, thats no problem
nnnnnnnnn...
xxxxxxxxxxxxxxxxxx... // <== x and n print 6000 times, this is to show writing to the buffer is no problem
23 // <=== the first iteration of the loop after copying in the "authorization: bearer " part
User exception (panic/abort/assert)
--------------- CUT HERE FOR EXCEPTION DECODER ---------------
Panic core_esp8266_main.cpp:191 __yield
>>>stack>>>
ctx: sys
sp: 3fffe660 end: 3fffffb0 offset: 0010
3fffe670: 3ffffe98 00000002 3fff291c 40209149
3fffe680: 3ffffe98 3fff291c 3fffe6e0 40201a83
3fffe690: 61222020 73656363 6f745f73 226e656b
3fffe6a0: 0022203a fffffffc 00000080 4020187a
3fffe6b0: 00000017 3ffe8842 3fff291c 3fff291c
3fffe6c0: 3fffff50 00000008 3fff291c 3fff291c
3fffe6d0: 3fffff50 3ffffde0 3ffffe98 40203470
3fffe6e0: 68747561 7a69726f 6f697461 42203a6e
3fffe6f0: 65726165 78652072 78787878 78787878
3fffe700: 78787878 78787878 78787878 78787878
3fffe710: 78787878 78787878 78787878 78787878
3fffe720: 78787878 78787878 78787878 78787878
3fffe730: 78787878 78787878 78787878 78787878
3fffe740: 78787878 78787878 78787878 78787878
3fffe750: 78787878 78787878 78787878 78787878
3fffe760: 78787878 78787878 78787878 78787878
3fffe770: 78787878 78787878 78787878 78787878
3fffe780: 78787878 78787878 78787878 78787878
3fffe790: 78787878 78787878 78787878 78787878
... repeats, the new ide makes it very hard to copy long stack traces, sorry
3ffffde0: 78787878 78787878 78787878 78787878
3ffffdf0: 78787878 78787878 78787878 78787878
3ffffe00: 78787878 78787878 78787878 78787878
3ffffe10: 78787878 78787878 78787878 78787878
3ffffe20: 78787878 78787878 78787878 78787878
3ffffe30: 78787878 78787878 78787878 78787878
3ffffe40: 78787878 78787878 78787878 00787878
3ffffe50: 00000000 00000000 00000000 4020a0ec
3ffffe60: 3ffffe94 fffffffc 00000000 4020a0ec
3ffffe70: 3ffffec0 fffffffc 00000064 3fff291c
3ffffe80: 00000004 4025ae24 00000000 4020830c
3ffffe90: 656e6f64 0019a2a7 4020caac 00000000
3ffffea0: 00001388 00001a28 00000000 00000000
3ffffeb0: 00000000 3fff38d4 3fff38d4 3fff3b4c
3ffffec0: 00001429 3fff2a8c 000000fa 000000fa
3ffffed0: 0000005c 000000fa 00001429 40209209
3ffffee0: 402529ce 3fff291c 3fff28e0 4020a0ec
3ffffef0: 3ffe8844 fffffffc 3ffe85f4 3fff28e0
3fffff00: 40207fcc 3ffe8842 3fff291c 4020a0ec
3fffff10: 3ffe8844 fffffffc 00000080 40203b2a
3fffff20: 00000001 3ffe8842 3fff291c 3fff2ab4
3fffff30: 3fffdad0 00000004 3fff291c 3fff2ab4
3fffff40: 3fffdad0 00000000 3fff291c 40203b9d
3fffff50: 3ffe85e0 feefef00 00000000 feefeffe
3fffff60: feefeffe feefeffe feefeffe feefeffe
3fffff70: feefeffe feefeffe feefeffe 3fffff5c
3fffff80: 00040000 feefffff feefeffe feefeffe
3fffff90: feefef00 feefeffe feefeffe feefeffe
3fffffa0: feefeffe feefeffe feefeffe feefeffe
<<<stack<<<
--------------- CUT HERE FOR EXCEPTION DECODER ---------------
ets Jan 8 2013,rst cause:4, boot mode:(3,6)
wdt reset
load 0x4010f000, len 3424, room 16
tail 0
chksum 0x2e
load 0x3fff20b8, len 40, room 8
tail 0
chksum 0x2b
Seems to be something wrong with the yield function, although I'm not sure what. Before it used to throw a watchdog error, and sometimes it also threw an exception 9 error, which would mean LoadStoreAlignmentCause.
I'll try to explain the error part in more detail
const char* headerStart = "authorization: Bearer "; // When I'll send the token, I'll need it to be in the HTTP header format
strcpy(header, headerStart); // copy over the beginning of the header
i = strlen(header); // get the index where the beginning part ends
while((c = readw(client)) != -1 && i < sizeof(header) - 1) {
if(c == '"') // if " is encountered stop copying, the token has ended
break;
header[i++] = c;
Serial.println(i);
yield(); //< line causing the crash!
}
header[i] = '\0';
Serial.println(header); // not once reached this part :(
I'd be very thankful for anyone willing to take a look at my code.
EDIT: Ran it through an exception decoder, the line causing the hangup is the yield line. But without it, watchdog crashes the program. What do I do?