ERIS CORE
svcSerialCommandInterface.cpp
Go to the documentation of this file.
1 /**
2  * @file svcSerialCommandInterface.h
3  * @author Brian Monkaba (brian.monkaba@gmail.com)
4  * @brief
5  * @version 0.1
6  * @date 2021-08-24
7  *
8  * @copyright Copyright (c) 2021
9  *
10  */
11 #include <ctype.h>
13 #include "AppManager.h"
14 #include "lz4.h"
15 #include <base64.hpp>
16 #include <pgmspace.h>
17 #include "ErisUtils.h"
18 #include <base64.hpp>
19 // AppSerialCommandInterface
20 //
21 /*
22 
23 INCOMMING MESSAGES:
24 
25  HELO
26  LS [PATH]
27  GET [PATH] (get file is responded with FS messages)
28  ACON (request current audio block connections)
29  CONNECT (make an audio block connection)
30  DISCONNECT (break an audio block connection)
31  AA (broadcast message to active app )
32  STATS
33  CQT_CFG request a dump of the cqt bin configs
34  GET_DD request a dump of the data dictionary
35  GET_RAM2 reqquest a dump of ram2
36  WREN_SCRIPT_START indicates the start of a bulk text transfer
37  WREN_SCRIPT_END ends the bulk text transfer
38  WREN_SCRIPT_EXECUTE executes the transferred script
39  WREN_SCRIPT_COMPILE compiles the transferred script and reports any errors
40 
41 OUTPUT MESSAGES:
42 
43  CQT_H (CQT BINS - High Range)
44  CQT_L (CQT " Low Range")
45  CQT_EOF (End of analysis frame)
46  DIR (directory contents - aka the result of the ls command)
47  DIR_EOF
48  S (FFT window stream)
49  FS (file stream)
50  FS_END (file stream end, indicate to client eof)
51  GET_ERR (error response )
52  ACON START (begin audio connection list)
53  ACON END (end audio connection list)
54  RAM {JSON}
55 
56 
57 */
58 //
59 extern SdFs sd;
60 
61 char* token;
62 const char newline = '\n';
63 
64 const char* gWelcomeMessage =
65 "M .▄▄▄▄▄▄▄▄▄▄▄..▄▄▄▄▄▄▄▄▄▄▄..▄▄▄▄▄▄▄▄▄▄▄..▄▄▄▄▄▄▄▄▄▄▄.\n"
66 "M ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌\n"
67 "M ▐░█▀▀▀▀▀▀▀▀▀.▐░█▀▀▀▀▀▀▀█░▌.▀▀▀▀█░█▀▀▀▀.▐░█▀▀▀▀▀▀▀▀▀.\n"
68 "M ▐░▌..........▐░▌.......▐░▌.....▐░▌.....▐░▌..........\n"
69 "M ▐░█▄▄▄▄▄▄▄▄▄.▐░█▄▄▄▄▄▄▄█░▌.....▐░▌.....▐░█▄▄▄▄▄▄▄▄▄.\n"
70 "M ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌.....▐░▌.....▐░░░░░░░░░░░▌\n"
71 "M ▐░█▀▀▀▀▀▀▀▀▀.▐░█▀▀▀▀█░█▀▀......▐░▌......▀▀▀▀▀▀▀▀▀█░▌\n"
72 "M ▐░▌..........▐░▌.....▐░▌.......▐░▌...............▐░▌\n"
73 "M ▐░█▄▄▄▄▄▄▄▄▄.▐░▌......▐░▌..▄▄▄▄█░█▄▄▄▄..▄▄▄▄▄▄▄▄▄█░▌\n"
74 "M ▐░░░░░░░░░░░▌▐░▌.......▐░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌\n"
75 "M .▀▀▀▀▀▀▀▀▀▀▀..▀.........▀..▀▀▀▀▀▀▀▀▀▀▀..▀▀▀▀▀▀▀▀▀▀▀.\n"
76 "M ....................................................\n"
77 "M .▄▄▄▄▄▄▄▄▄▄▄..▄▄▄▄▄▄▄▄▄▄▄..▄▄▄▄▄▄▄▄▄▄▄..▄▄▄▄▄▄▄▄▄▄▄.\n"
78 "M ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌\n"
79 "M ▐░█▀▀▀▀▀▀▀▀▀.▐░█▀▀▀▀▀▀▀█░▌▐░█▀▀▀▀▀▀▀█░▌▐░█▀▀▀▀▀▀▀▀▀.\n"
80 "M ▐░▌..........▐░▌.......▐░▌▐░▌.......▐░▌▐░▌..........\n"
81 "M ▐░▌..........▐░▌.......▐░▌▐░█▄▄▄▄▄▄▄█░▌▐░█▄▄▄▄▄▄▄▄▄.\n"
82 "M ▐░▌..........▐░▌.......▐░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌\n"
83 "M ▐░▌..........▐░▌.......▐░▌▐░█▀▀▀▀█░█▀▀.▐░█▀▀▀▀▀▀▀▀▀.\n"
84 "M ▐░▌..........▐░▌.......▐░▌▐░▌.....▐░▌..▐░▌..........\n"
85 "M ▐░█▄▄▄▄▄▄▄▄▄.▐░█▄▄▄▄▄▄▄█░▌▐░▌......▐░▌.▐░█▄▄▄▄▄▄▄▄▄.\n"
86 "M ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░▌.......▐░▌▐░░░░░░░░░░░▌\n"
87 "M .▀▀▀▀▀▀▀▀▀▀▀..▀▀▀▀▀▀▀▀▀▀▀..▀.........▀..▀▀▀▀▀▀▀▀▀▀▀.\n"
88 "M Connected to the Serial Command Interface \n";
89 
90 
91 int FLASHMEM replacechar(char *str, char orig, char rep) {
92  char *ix = str;
93  int n = 0;
94  while((ix = strchr(ix, orig)) != NULL) {
95  *ix++ = rep;
96  n++;
97  }
98  return n;
99 }
100 
101 uint16_t FLASHMEM SvcSerialCommandInterface::checksum(const char *msg){
102  uint16_t csum;
103  csum=0;
104  for(uint16_t i = 0;i<strlen(msg);i++){
105  csum += msg[i];
106  }
107  return csum;
108 }
109 
110 /**
111  * @brief Calling this function signals the end of a compressed message.\n
112  * The txBuffer contents are lz4 compressed, base64 encoded and finally sent.\n
113  * The serial message format is "LZ4 [UNCOMPRESSED_SIZE] [BASE64 encoded LZ4 compressed message]"
114  */
116  uint16_t compressed_len,uncompressed_len;
117 
118  while(throttle()){
119  delay(5);
120  };
121 #ifdef USE_EXTMEM
122  //do nothing - one time malloc in the constructor
123 #else
124  char workingBuffer[SERIAL_WORKING_BUFFER_SIZE];
125 #endif
126  am->requestArmSetClock(CPU_BOOST_MAX_FREQ);
127  memset(p_working_buffer,0,SERIAL_WORKING_BUFFER_SIZE);
128  uncompressed_len = index_tx_buffer;
129  compressed_len = LZ4_compress_fast(p_tx_Buffer,p_working_buffer,index_tx_buffer,SERIAL_WORKING_BUFFER_SIZE,1);
130  empty();
131  encode_base64((unsigned char *)p_working_buffer, compressed_len, (unsigned char *)p_tx_Buffer);
132 
133  Serial.print("LZ4 ");
134  Serial.print(uncompressed_len);
135  Serial.print(" ");
136  Serial.println(p_tx_Buffer);
137  am->requestArmSetClock(CPU_BASE_FREQ);
138 
139 #ifdef USE_EXTMEM
140  //do nothing - one time malloc in the constructor
141 #else
142  //do nothing - local var
143 #endif
144 }
145 
146 /**
147  * @brief immediately transmit then clear the txBuffer
148  *
149  */
151  while(throttle()){
152  delay(5);
153  }
154  if (strlen(p_tx_Buffer) > 0 ) Serial.print(p_tx_Buffer);
155  memset(p_tx_Buffer,0,SERIAL_OUTPUT_BUFFER_SIZE);
156  index_tx_buffer = 0;
157  tx_buffer_overflow_flag = false;
158 }
159 
160 /**
161  * @brief returns true if the available serial buffer falls below SERIAL_THROTTLE_BUFFER_REMAINING_THRESHOLD
162  * if the buffer falls below SERIAL_THROTTLE_CHECK_CONNECTION_BUFFER_THRESHOLD delay for a small period of time
163  * if another check finds the buffer isn't moving then flush the buffer.
164  * returns false otherwise
165  *
166  * @return true
167  * @return false
168  */
170  uint16_t avail;
171  avail = Serial.availableForWrite();
172 
173  if(avail < SERIAL_THROTTLE_CHECK_CONNECTION_BUFFER_THRESHOLD){
174  uint16_t delta_avail;
175  uint16_t msec_delay_counter = 0;
176  //serial tx buffer not available
177  do{
178  delay(5);
179  msec_delay_counter += 5;
180  delta_avail = Serial.availableForWrite();
181  if (delta_avail > SERIAL_THROTTLE_CHECK_CONNECTION_BUFFER_THRESHOLD) break;
182  } while (msec_delay_counter < SERIAL_THROTTLE_CHECK_CONNECTION_MAX_DELAY);
183 
184  if(avail == delta_avail){
185  return true;
186  } else{
187  //Serial.println(F("M WRN throttling"));//use this for debug only!
188  am->data->increment("SERIAL_THROTTLE_EVENTS");
189  }
190  }
191  if(avail < SERIAL_THROTTLE_BUFFER_THRESHOLD) return true;
192  return false;
193 }
194 
196  char* s;
197  s = strrchr(p_tx_Buffer,'\r');//find last new line
198  s[0] = 0;
199  token = strtok(p_tx_Buffer, &newline);
200  while( token != NULL ) {
201  while(throttle()){
202  delay(50);
203  }
204  if (tx_buffer_overflow_flag==false){
205  //txBufferOverflowFlag=true;
206  Serial.print(F("\""));
207  }else{
208  Serial.print(F(",\""));
209  }
210  //replaceAll(token,'\r',0);
211  Serial.print(token);
212  Serial.print(F("\""));
213  token = strtok(NULL, &newline);
214  }
215  //close the tag
216  Serial.println("]");
217  delay(120);
218  Serial.printf("VM remainder %s",p_tx_Buffer);
219  //start the next part of the multipart message
220  Serial.print(&p_multipart_header[0]);
221  s = strrchr(p_tx_Buffer,'\r');//find last new line
222  //s = strrchr(s,'\r');//find last new line
223  s+=2;
224  //Serial.flush();
225  index_tx_buffer = strlen(s);
227 
228 }
229 
230 
232  Serial.println(F("M SvcSerialCommandInterface::streamReceiveHandler isCapturingBulkData"));
233  char* f;
234  do{
235  do{
236  p_capture_buffer[index_capture_buffer] = Serial.read();
238  }while (Serial.available() && index_capture_buffer < SERIAL_RX_CAPTURE_BUFFER_SIZE-1);//in case were asking for the streaming data faster than it's being sent
239  if(index_capture_buffer == SERIAL_RX_CAPTURE_BUFFER_SIZE-1){
240  Serial.println(F("M SvcSerialCommandInterface::streamReceiveHandler Wren Script ERROR: Input buffer full"));
241  Serial.flush();
242  is_capturing_bulk_data = false;
244  }
245  f = strstr(p_capture_buffer,"WREN_SCRIPT_END");
246  if (f > 0){
247  Serial.println(F("M SvcSerialCommandInterface::streamReceiveHandler Wren Script Received"));
248  memset(f,0,strlen("WREN_SCRIPT_END"));//remove the EOF marker
249  //stream complete
250  is_capturing_bulk_data = false;
251  }
252  }while(is_capturing_bulk_data);
253 }
254 
255 
257  char bufferChr; //only need one char - 512 size is due to a potential bug in the teensy library identified by covintry static analysis
258  char hexBuffer[8];
259  uint16_t payload_len;
260  //if(Serial.availableForWrite() < 6000) return;
261  if(throttle()) return;
262  p_SD->chdir(p_stream_path);
263  if (p_SD->exists(p_stream_file)){
264  payload_len = 0;
265  if(!file.open(p_stream_file, O_READ)){
266  Serial.println(F("M GET_ERR FILE OPEN ERROR"));
267  is_streaming_file = false;
269  stream_pos = 0;
270  return;
271  }
272  startLZ4Message();
273  print("FS ");
274  file.seek(stream_pos);
275  //send the next chunked message until eof
276  uint64_t i;
277  i = file.available();
278  if(i>0){
279  //if i is zero then last chunk was eof
280  if(i > SERIAL_FILESTREAM_PAYLOAD_SIZE) i = SERIAL_FILESTREAM_PAYLOAD_SIZE;
281  for(;i > 0; i--){
282  payload_len += 1;
283  if (file.read(&bufferChr,1) < 0){
284  Serial.println(F("\nM GET_ERR FILE READ ERROR"));
285  is_streaming_file = false;
287  stream_pos = 0;
288  file.close();
289  return;
290  }
291  sprintf(hexBuffer,"%02X,",(unsigned int)bufferChr);
292  print(hexBuffer);
293  }
294  if(index_tx_buffer > 0) p_tx_Buffer[--index_tx_buffer] = '\0'; //remove last comma
295  stream_pos = file.position();
296  println("");
297  }
298  file.close();
299  if (payload_len == 0){
300  //no data to tx; @ eof
301  sendLZ4Message();
302  Serial.println("FS_END");
303  is_streaming_file = false;
305  stream_pos = 0;
306  delayMicroseconds(3000);
307  return;
308  }
309  else if (payload_len < SERIAL_FILESTREAM_PAYLOAD_SIZE) {
310  sendLZ4Message();
311  Serial.println("FS_END");
312  empty();
313  is_streaming_file = false;
315  stream_pos = 0;
316  delayMicroseconds(3000);
317  return;
318  } else{
319  //send file chunk
320  sendLZ4Message();
321  return;
322  }
323  }
324  else{
325  Serial.print(F("M GET_ERR FILE NOT FOUND "));
326  Serial.println(p_stream_file);
327  is_streaming_file = false;
328  stream_pos = 0;
329  return;
330  }
331 };
332 
333 
335  char* mp = 0;
336  char c;
337 #ifdef USE_EXTMEM
338  char tmp[1000];
339  //tmp = (char*)extmem_malloc(1000);
340 #else
341  char* tmp;
342  tmp = (char*)malloc(1000);
343 #endif
344  while(throttle()){delay(100);}
345  if(!requestStartLZ4Message()) return;
346  strcpy(tmp,"");
347  printf(F("RAM {\"RAM1\":{\"addr\":\"%08X\",\"chunk\":\""),0x000000);
348 
349  for(uint32_t i = 0x00070000; i < 0x0007F000; i+=1){
350  //if (i == 0x20010000) i = 0x20200000;
351  mp = (char*)i;
352  c = *mp;
353  c = (c & 0xFF);
354  if(i%32==0 && i != 0x00070000){
355  print(F("\",\"decode\":\""));
356  print(tmp);
357  println(F("\"}}"));
358  sendLZ4Message();
359  strcpy(tmp,"");
360  while(throttle()){delay(150);}
361  if(i%1024==0){
362  float32_t pct;
363  pct = 100.0 * ((float)(i-0x00070000)/(float)(0x0007F000));
364  Serial.printf(F("CLS\nM GET_RAM1 %08X %.0f pct "),i,pct);
365  for(uint16_t div = 0; div < (uint16_t)pct; div += 5){
366  Serial.printf("*");
367  }
368  Serial.println("");
369  }
370  startLZ4Message();
371  printf(F("RAM {\"RAM1\":{\"addr\":\"%08X\",\"chunk\":\""),i);
372  }
373 
374  printf("%02X ",(uint8_t)c);
375  const char* escape = "\\";
376  if (c=='"'){
377  strncat(tmp, escape, 1);
378  strncat(tmp, &c, 1);
379  }else if (c=='\''){
380  strncat(tmp, escape, 1);
381  strncat(tmp, &c, 1);
382  }else if (isprint((int)c)){
383  strncat(tmp, &c, 1);
384  }else if (iscntrl((int)c)){
385  c = '.';
386  strncat(tmp, &c, 1);
387  }else{
388  c = '?';
389  strncat(tmp, &c, 1);
390  }
391  }
392  println("\n");
393  println(F("RAM END"));
394  sendLZ4Message();
395 #ifdef USE_EXTMEM
396  //do nothing - local var
397 #else
398  free(tmp);
399 #endif
400 }
401 
403  char* mp = 0;
404  char c;
405 #ifdef USE_EXTMEM
406  char tmp[1000];
407  //tmp = (char*)extmem_malloc(1000);
408 #else
409  char* tmp;
410  tmp = (char*)malloc(1000);
411 #endif
412  while(throttle()){delay(100);}
413  if(!requestStartLZ4Message()) return;
414  strcpy(tmp,"");
415  printf(F("RAM {\"RAM2\":{\"addr\":\"%08X\",\"chunk\":\""),0x20000000);
416  //RAM2 always ends at 0x20280000
417  for(uint32_t i = 0x20000000; i < 0x20280000; i+=1){
418  if (i == 0x20010000) i = 0x20200000;
419  mp = (char*)i;
420  c = *mp;
421  c = (c & 0xFF);
422  if(i%32==0 && i != 0x20000000){
423  print(F("\",\"decode\":\""));
424  print(tmp);
425  println(F("\"}}"));
426  sendLZ4Message();
427  strcpy(tmp,"");
428  while(throttle()){delay(50);}
429  if(i%1024==0){
430  float32_t pct;
431  pct = 100.0 * ((float)(i-0x20000000)/(float)(0x20280000-0x20000000));
432  Serial.printf(F("CLS\nM GET_RAM2 %08X %.0f pct "),i,pct);
433  for(uint16_t div = 0; div < (uint16_t)pct; div += 5){
434  Serial.printf("*");
435  }
436  Serial.println("");
437  }
438  startLZ4Message();
439  printf(F("RAM {\"RAM2\":{\"addr\":\"%08X\",\"chunk\":\""),i);
440  }
441 
442  printf("%02X ",(uint8_t)c);
443  const char* escape = "\\";
444  if (c=='"'){
445  strncat(tmp, escape, 1);
446  strncat(tmp, &c, 1);
447  }else if (c=='\''){
448  strncat(tmp, escape, 1);
449  strncat(tmp, &c, 1);
450  }else if (isprint((int)c)){
451  strncat(tmp, &c, 1);
452  }else if (iscntrl((int)c)){
453  c = '.';
454  strncat(tmp, &c, 1);
455  }else{
456  c = '?';
457  strncat(tmp, &c, 1);
458  }
459  }
460  println("\n");
461  println(F("RAM END"));
462  sendLZ4Message();
463 #ifdef USE_EXTMEM
464  //do nothing - local var
465 #else
466  free(tmp);
467 #endif
468 }
469 
471  int total_read;
472  char cmd[128], param[128];
473  int32_t val;
474  float32_t fval;
475  total_read = sscanf(p_received_chars, "%127s %127s %d" , cmd, param,(int*)&val);
476  if (total_read < 3){
477  total_read = sscanf(p_received_chars, "%127s %127s %f" , cmd, param,(float32_t*)&fval);
478  if (total_read < 3){
479  Serial.print(F("M SvcSerialCommandInterface::messageHandler_UPDATE_DD ERROR WRONG PARAM COUNT"));
480  Serial.println(total_read);
481  } else am->data->update(param,fval);
482  }else if(!am->data->update(param,val)){
483  total_read = sscanf(p_received_chars, "%127s %127s %f" , cmd, param,(float32_t*)&fval);
484  am->data->update(param,fval);
485  }
486 }
487 
488 
490  if(p_capture_buffer != NULL){
491  Serial.println(F("M SvcSerialCommandInterface::messageHandler_WREN_SCRIPT_SAVE: script save request"));
492  am->sendMessage(this,"AppWren",p_received_chars);//"WREN_SCRIPT_SAVE [modulename]");
493  am->sendMessage(this,"AppWren",p_capture_buffer);
494  #ifndef USE_EXTMEM
495  captureBuffer = (char*)realloc(captureBuffer,0);
496  Serial.println(F("M SvcSerialCommandInterface::messageHandler_WREN_SCRIPT_SAVE: captureBuffer released"));
497  #endif
498  } else Serial.println(F("M SvcSerialCommandInterface::messageHandler_WREN_SCRIPT_SAVE: captureBuffer is NULL"));
499 }
500 
502  if(p_capture_buffer != NULL){
503  Serial.println(F("M SvcSerialCommandInterface::messageHandler_WREN_SCRIPT_EXECUTE: script execute request"));
504  am->sendMessage(this,"AppWren","WREN_SCRIPT_EXECUTE");
505  am->sendMessage(this,"AppWren",p_capture_buffer);
506 #ifndef USE_EXTMEM
507  captureBuffer = (char*)realloc(captureBuffer,0);
508  Serial.println(F("M SvcSerialCommandInterface::messageHandler_WREN_SCRIPT_EXECUTE: captureBuffer released"));
509 #endif
510  } else Serial.println(F("M SvcSerialCommandInterface::messageHandler_WREN_SCRIPT_EXECUTE: captureBuffer is NULL"));
511 }
512 
513 
515  if(p_capture_buffer != NULL){
516  Serial.println(F("M SvcSerialCommandInterface::messageHandler_WREN_SCRIPT_COMPILE: script compile request"));
517  am->sendMessage(this,"AppWren","WREN_SCRIPT_COMPILE");
518  am->sendMessage(this,"AppWren",p_capture_buffer);
519 #ifndef USE_EXTMEM
520  captureBuffer = (char*)realloc(captureBuffer,0);
521  Serial.println(F("M SvcSerialCommandInterface::messageHandler_WREN_SCRIPT_COMPILE: captureBuffer released"));
522 #endif
523  } else Serial.println(F("M SvcSerialCommandInterface::messageHandler_WREN_SCRIPT_COMPILE: captureBuffer is NULL"));
524 }
525 
527  is_capturing_bulk_data = true;
528 #ifndef USE_EXTMEM
529  char* tmp;
530  tmp = (char*)realloc(captureBuffer,SERIAL_RX_CAPTURE_BUFFER_SIZE);
531  if(tmp==NULL){
532  //realloc failed; release the pointer and try again
533  free(captureBuffer);
534  captureBuffer = (char*)malloc(SERIAL_RX_CAPTURE_BUFFER_SIZE);
535  } else captureBuffer = tmp;
536 #endif
537  if(p_capture_buffer!=NULL){
538  memset(p_capture_buffer,0,SERIAL_RX_CAPTURE_BUFFER_SIZE);
540  Serial.println(F("M SvcSerialCommandInterface::messageHandler_WREN_SCRIPT_START"));
541  } else{
542  is_capturing_bulk_data = false;
543  Serial.println(F("M SvcSerialCommandInterface::messageHandler_WREN_SCRIPT_START ERROR: realloc(SERIAL_RX_CAPTURE_BUFFER_SIZE) failed"));
544  Serial.flush();
545  }
546 }
547 
549  int total_read;
550  char cmd[128], param[128],param2[128];
551  total_read = sscanf(p_received_chars, "%127s %127s %127s" , cmd, param,param2);
552  if (total_read < 3){
553  Serial.print(F("M GET_ERR WRONG PARAM COUNT"));
554  Serial.println(param);
555  } else{
556  while(throttle()){ delay(1);}
557  //file streaming request ok
558  //init the transfer
559  Serial.print(F("M GET_OK "));
560  Serial.print(param);
561  Serial.print(" ");
562  Serial.println(param2);
563  strcpy(p_stream_path,param);
564  strcpy(p_stream_file,param2);
565  Serial.println(F("FS_START"));
566  is_streaming_file = true;
568  stream_pos = 0;
569  }
570 }
572  bool first;
573  int total_read;
574  char cmd[128], param[128],param2[128];
575  total_read = sscanf(p_received_chars, "%127s %127s %127s" , cmd, param,param2);
576  while(Serial.availableForWrite() < 6000){
577  delay(20);
578  }
579  if (total_read < 2){
580  //send the root directory
581  //delay(100);
582  empty();
583  println(F("DIR"));
584  print(F("LS . ["));
585 #ifdef USE_EXTMEM
586  //do nothing - one time malloc in the constructor
587 #else
588  workingBuffer = (char*)malloc(SERIAL_WORKING_BUFFER_SIZE);
589 #endif
591  empty();
592  Serial.print(p_working_buffer);
593  sd->chdir();
594  tx_buffer_overflow_flag = false;
595  sd->ls(this);
596  memset(p_working_buffer,0,SERIAL_WORKING_BUFFER_SIZE);
598  empty();
599  first = true;
600  token = strtok(p_working_buffer, &newline);
601  while( token != NULL ) {
602  if (first && !tx_buffer_overflow_flag) Serial.print(F("\""));
603  else Serial.print(F(",\""));
604  Serial.print(token);
605  Serial.print(F("\""));
606  token = strtok(NULL, &newline);
607  first = false;
608  }
609  Serial.println(F("]"));
610  Serial.println(F("DIR_EOF"));
611 #ifdef USE_EXTMEM
612  //do nothing - one time malloc in the constructor
613 #else
614  free(workingBuffer);
615 #endif
616  //delay(200);
617  } else{
618  //send the requested path
619  replacechar(param,':',' '); //replace space token used to tx the path
620  empty();
621  print(F("M "));
622  println(param);
623  println(F("DIR"));
624  send();
625  sd->chdir();
626  replacechar(param,' ',':'); //replace any spaces in the transmitted path to ':'
627  sprintf(p_multipart_header, "LS %s [", param);
628  Serial.print(p_multipart_header);
629  replacechar(param,':',' '); // put the spaces back in time for the ls command
630  sd->ls(this,param,false);
631  first = true;
632  token = strtok(p_tx_Buffer, &newline);
633  while( token != NULL && index_tx_buffer > 1) {
634  if (first && !tx_buffer_overflow_flag) Serial.print(F("\""));
635  else Serial.print(F(",\""));
636  replacechar(token,'\r',0);
637  Serial.print(token);
638  Serial.print(F("\""));
639  token = strtok(NULL, &newline);
640  first = false;
641  while(throttle()){
642  delay(50);
643  }
644  }
645  Serial.println(F("]"));
646  Serial.println(F("DIR_EOF"));
647  }
648 }
649 
651  char endMarker = '\n';
652  boolean newRxMsg = false;
653  char bufferChr;
654 
655  if (is_streaming_file){ //streaming file handler
657  }
658 
659  if(is_capturing_bulk_data){ //bulk data state handler
661  }else { //check for incoming data
662  //This is the incoming message handler - it collects the message one byte at a time until
663  //the end of message marker. If a full message is received the newRxMsg flag is set true.
664  //if a partial message is received the flag remains false.
665  //if the receiving buffer reaches an overflow condition, reset the buffer and clear in remaining serial input
666  if (Serial.available() > 0 && false == newRxMsg ){
667  do{
668  bufferChr = Serial.read();
669  p_received_chars[index_rx_buffer++] = bufferChr;
670 
671  if(index_rx_buffer >= (SERIAL_RX_BUFFER_SIZE-2)){
672  //input overflow - clear serial input and reset the index
673  index_rx_buffer = 0;
674  Serial.clear();
675  Serial.clearReadError();
676  return;
677  }
678 
679  if (bufferChr == endMarker){
680  p_received_chars[--index_rx_buffer] = '\0'; //remove the end marker and null terminate the string
681  index_rx_buffer = 0; //reset the input write index
682  newRxMsg = true;
683  }
684  } while (Serial.available() > 0 && false == newRxMsg);
685  }
686  if (newRxMsg){
687  //Once a new message is received, first the command is parsed out from the message
688  // this first split into cmd and param is for the purpose of identifying the requested command
689  // each individual message handler is then responsible for parsing out their own parameters if required.
690  char cmd[128], param[128],param2[128];
691  int total_read;
692  total_read = sscanf(p_received_chars, "%127s %127s" , cmd, param);
693  while(throttle()){delay(10);}; //let the transmitt buffer clear out
694  if (strncmp(cmd, "LS",sizeof(cmd)) == 0){
696  }else if (strncmp(cmd, "GET",sizeof(cmd)) == 0){
698  }else if (strncmp(cmd, "ACON",sizeof(cmd)) == 0){ //audio connections
699  uint16_t i;
700  char csBuffer[128];
701  i = 0;
702  Serial.println(F("M ACON START"));
703 
704  while(ad->getConnectionString(i,csBuffer)){
705  i += 1;
706  Serial.print("M ");
707  Serial.println(csBuffer);
708  }
709  Serial.println(F("M ACON END"));
710 
711  }else if (strncmp(cmd, "CONNECT",sizeof(cmd)) == 0){
712  int source_port;
713  int dest_port;
714 
715  total_read = sscanf(p_received_chars, "%127s %127s %d %127s %d" , cmd, param,&source_port,param2,&dest_port);
716  if (total_read < 3){
717  Serial.print(F("M CONNECT WRONG PARAM COUNT "));
718  Serial.println(p_received_chars);
719  } else{
720  ad->connect(param,source_port,param2,dest_port);
721  }
722  }else if (strncmp(cmd, "DISCONNECT",sizeof(cmd)) == 0){
723  int dest_port;
724  total_read = sscanf(p_received_chars, "%127s %127s %d" , cmd, param,&dest_port);
725  if (total_read < 2){
726  Serial.print(F("M DISCONNECT WRONG PARAM COUNT "));
727  Serial.println(p_received_chars);
728  } else{
729  if (ad->disconnect(param,dest_port)){
730  Serial.printf(F("M DISCONNECTED %s %d\n"),param,dest_port);
731  }else{
732  Serial.printf(F("M FAILED DISCONNECT OF %s %d\n"),param,dest_port);
733  }
734  }
735  }else if (strncmp(cmd, "AA",sizeof(cmd)) == 0){ //active app message
736  if (total_read > 1) am->getActiveApp()->messageHandler(this,param);
737  }else if (strncmp(cmd, "APC",sizeof(cmd)) == 0){
738  //forward the message to the SvcErisAudioParameterController
739  am->getAppByName("APC")->messageHandler(this,p_received_chars + 4);
740  }else if (strncmp(cmd, "STATS",sizeof(cmd)) == 0){
741  ad->printStats();
742  while(throttle()){delay(1);}
743  am->printStats();
744  }else if (strncmp(cmd, "CQT_CFG",sizeof(cmd)) == 0){
745  am->sendMessage(this,"AppCQT","CQT_INFO");
746  }else if (strncmp(cmd, "GET_DD",sizeof(cmd)) == 0){
747  am->data->printDictionary(this);
748  }else if (strncmp(cmd, "GET_WREN_SCRIPT",sizeof(cmd)) == 0){
749  while(throttle()){delay(100);}
751  println("#WREN_START!");
752  println(g_wrenScript);
753  println("#WREN_EOF!");
754  sendLZ4Message();
755  }
756  }else if (strncmp(cmd, "WREN_SCRIPT_START",sizeof(cmd)) == 0){
758  }else if (strncmp(cmd, "WREN_SCRIPT_COMPILE",sizeof(cmd)) == 0){
760  }else if (strncmp(cmd, "WREN_SCRIPT_EXECUTE",sizeof(cmd)) == 0){
762  }else if (strncmp(cmd, "WREN_SCRIPT_SAVE",sizeof(cmd)) == 0){
764  }else if (strncmp(cmd, "UPDATE_DD",sizeof(cmd)) == 0){
766  }else if (strncmp(cmd, "HELO",sizeof(cmd)) == 0){
768  println(gWelcomeMessage);
769  sendLZ4Message();
770  }
771  }else if (strncmp(cmd, "GET_RAM",sizeof(cmd)) == 0){
772  char* mp;
773  long num;
774  num = strtol(param,NULL,10);
775  mp = (char*)num;
776  Serial.printf("M GET_RAM {\"addr\":\"%08X\",\"data\":\"%02X\"}\n",num,(uint8_t)*mp);
777  }else if (strncmp(cmd, "GET_RAM2",sizeof(cmd)) == 0){
779  }else if (strncmp(cmd, "GET_RAM1",sizeof(cmd)) == 0){
781  }else if (strncmp(cmd, "AUDIO_NO_INTERRUPTS",sizeof(cmd)) == 0){
782  AudioNoInterrupts();
783  }else if (strncmp(cmd, "AUDIO_INTERRUPTS",sizeof(cmd)) == 0){
784  AudioInterrupts();
785  }
786  //END
787  newRxMsg = false;
788 
789  #ifdef SERIAL_AUTO_TRANSMIT_DATA_PERIODICALLY
791  if (et_since_periodic_stats_tx > SERIAL_AUTO_TRANSMIT_STATS_PERIOD){
794  ad->printStats();
795  }else if (et_since_periodic_data_dict_tx > SERIAL_AUTO_TRANSMIT_DATA_DICT_PERIOD){
798  }
799  }
800  #else
801  }
802  #endif
803  }
805 }; //allways called even if app is not active
const char PROGMEM s[][16]
Definition: Eris.h:246
bool safer_strncpy(char *dest, const char *source, uint16_t dest_size)
a safer strncpy
Definition: ErisUtils.cpp:20
AppManager * am
Definition: AppBaseClass.h:38
AudioDirector * ad
Definition: AppBaseClass.h:37
virtual void messageHandler(AppBaseClass *sender, const char *message)
receiver method for inter-app string based communication
Definition: AppBaseClass.h:248
AppBaseClass * getAppByName(const char *appName)
Get the App pointer By Name. Returns NULL if not found.
Definition: AppManager.cpp:489
SvcDataDictionary * data
Definition: AppManager.h:63
bool sendMessage(AppBaseClass *sender, const char *to_app, const char *message)
provides an interface for apps to send messages to other apps
Definition: AppManager.cpp:469
static AppManager * getInstance()
Definition: AppManager.h:66
void printStats()
prints out some stats in JSON format to the serial port
Definition: AppManager.cpp:504
AppBaseClass * getActiveApp()
provides an interface for apps to request the active app object
Definition: AppManager.cpp:458
bool requestArmSetClock(uint32_t requested_cpu_frequency)
provides an interface for apps to request cpu freq change note: this is only a request....
Definition: AppManager.cpp:603
bool getConnectionString(uint16_t connectionIndex, char *connectionStringBuffer)
bool disconnect(AudioStream *destination, int destinationInput)
bool connect(AudioStream *source, int sourceOutput, AudioStream *destination, int destinationInput)
bool increment(const char *key)
increments the value of a global record creates a new record if one does not exist and initializes ...
bool update(const char *key, int32_t val, uint32_t *owner)
update the value of an owned record creates a new record if one does not exist and initializes its ...
void printDictionary(SvcSerialCommandInterface *sci)
prints the dictionary to the SvcSerialCommandInterface
uint16_t checksum(const char *msg)
bool requestStartLZ4Message()
request to start a lz4 compressed message starts the message and returns true if available returns f...
bool throttle()
returns true if the available serial buffer falls below SERIAL_THROTTLE_BUFFER_REMAINING_THRESHOLD if...
void send()
immediately transmit then clear the txBuffer
void empty()
zero out the transmit buffer and reset the write index
void update() override
update loop
void sendLZ4Message()
Calling this function signals the end of a compressed message. The txBuffer contents are lz4 compres...
const char * gWelcomeMessage
SdFs sd
const char newline
int FLASHMEM replacechar(char *str, char orig, char rep)