Fork of Smoothie to port to mbed non-LPC targets.
Fork of Smoothie by
SimpleShell.cpp
00001 /* 00002 This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). 00003 Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 00004 Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 00005 You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. 00006 */ 00007 00008 00009 #include "libs/Kernel.h" 00010 #include "SimpleShell.h" 00011 #include "libs/nuts_bolts.h" 00012 #include "libs/utils.h" 00013 #include "libs/SerialMessage.h" 00014 #include "libs/StreamOutput.h" 00015 #include "modules/robot/Conveyor.h" 00016 #include "DirHandle.h" 00017 #include "mri.h" 00018 #include "version.h" 00019 #include "PublicDataRequest.h" 00020 #include "FileStream.h" 00021 00022 #include "modules/tools/temperaturecontrol/TemperatureControlPublicAccess.h" 00023 #include "modules/robot/RobotPublicAccess.h" 00024 #include "NetworkPublicAccess.h" 00025 #include "platform_memory.h" 00026 00027 extern unsigned int g_maximumHeapAddress; 00028 00029 #include <malloc.h> 00030 #include <mri.h> 00031 #include <stdio.h> 00032 #include <stdint.h> 00033 00034 extern "C" uint32_t __end__; 00035 extern "C" uint32_t __malloc_free_list; 00036 extern "C" uint32_t _sbrk(int size); 00037 00038 #define get_temp_command_checksum CHECKSUM("temp") 00039 #define get_pos_command_checksum CHECKSUM("pos") 00040 00041 // command lookup table 00042 SimpleShell::ptentry_t SimpleShell::commands_table[] = { 00043 {CHECKSUM("ls"), &SimpleShell::ls_command}, 00044 {CHECKSUM("cd"), &SimpleShell::cd_command}, 00045 {CHECKSUM("pwd"), &SimpleShell::pwd_command}, 00046 {CHECKSUM("cat"), &SimpleShell::cat_command}, 00047 {CHECKSUM("rm"), &SimpleShell::rm_command}, 00048 {CHECKSUM("reset"), &SimpleShell::reset_command}, 00049 {CHECKSUM("dfu"), &SimpleShell::dfu_command}, 00050 {CHECKSUM("break"), &SimpleShell::break_command}, 00051 {CHECKSUM("help"), &SimpleShell::help_command}, 00052 {CHECKSUM("?"), &SimpleShell::help_command}, 00053 {CHECKSUM("version"), &SimpleShell::version_command}, 00054 {CHECKSUM("mem"), &SimpleShell::mem_command}, 00055 {CHECKSUM("get"), &SimpleShell::get_command}, 00056 {CHECKSUM("set_temp"), &SimpleShell::set_temp_command}, 00057 {CHECKSUM("net"), &SimpleShell::net_command}, 00058 {CHECKSUM("load"), &SimpleShell::load_command}, 00059 {CHECKSUM("save"), &SimpleShell::save_command}, 00060 00061 // unknown command 00062 {0, NULL} 00063 }; 00064 00065 // Adam Greens heap walk from http://mbed.org/forum/mbed/topic/2701/?page=4#comment-22556 00066 static uint32_t heapWalk(StreamOutput *stream, bool verbose) 00067 { 00068 uint32_t chunkNumber = 1; 00069 // The __end__ linker symbol points to the beginning of the heap. 00070 uint32_t chunkCurr = (uint32_t)&__end__; 00071 // __malloc_free_list is the head pointer to newlib-nano's link list of free chunks. 00072 uint32_t freeCurr = __malloc_free_list; 00073 // Calling _sbrk() with 0 reserves no more memory but it returns the current top of heap. 00074 uint32_t heapEnd = _sbrk(0); 00075 // accumulate totals 00076 uint32_t freeSize = 0; 00077 uint32_t usedSize = 0; 00078 00079 stream->printf("Used Heap Size: %lu\n", heapEnd - chunkCurr); 00080 00081 // Walk through the chunks until we hit the end of the heap. 00082 while (chunkCurr < heapEnd) { 00083 // Assume the chunk is in use. Will update later. 00084 int isChunkFree = 0; 00085 // The first 32-bit word in a chunk is the size of the allocation. newlib-nano over allocates by 8 bytes. 00086 // 4 bytes for this 32-bit chunk size and another 4 bytes to allow for 8 byte-alignment of returned pointer. 00087 uint32_t chunkSize = *(uint32_t *)chunkCurr; 00088 // The start of the next chunk is right after the end of this one. 00089 uint32_t chunkNext = chunkCurr + chunkSize; 00090 00091 // The free list is sorted by address. 00092 // Check to see if we have found the next free chunk in the heap. 00093 if (chunkCurr == freeCurr) { 00094 // Chunk is free so flag it as such. 00095 isChunkFree = 1; 00096 // The second 32-bit word in a free chunk is a pointer to the next free chunk (again sorted by address). 00097 freeCurr = *(uint32_t *)(freeCurr + 4); 00098 } 00099 00100 // Skip past the 32-bit size field in the chunk header. 00101 chunkCurr += 4; 00102 // 8-byte align the data pointer. 00103 chunkCurr = (chunkCurr + 7) & ~7; 00104 // newlib-nano over allocates by 8 bytes, 4 bytes for the 32-bit chunk size and another 4 bytes to allow for 8 00105 // byte-alignment of the returned pointer. 00106 chunkSize -= 8; 00107 if (verbose) 00108 stream->printf(" Chunk: %lu Address: 0x%08lX Size: %lu %s\n", chunkNumber, chunkCurr, chunkSize, isChunkFree ? "CHUNK FREE" : ""); 00109 00110 if (isChunkFree) freeSize += chunkSize; 00111 else usedSize += chunkSize; 00112 00113 chunkCurr = chunkNext; 00114 chunkNumber++; 00115 } 00116 stream->printf("Allocated: %lu, Free: %lu\r\n", usedSize, freeSize); 00117 return freeSize; 00118 } 00119 00120 00121 void SimpleShell::on_module_loaded() 00122 { 00123 this->register_for_event(ON_CONSOLE_LINE_RECEIVED); 00124 this->register_for_event(ON_GCODE_RECEIVED); 00125 this->register_for_event(ON_SECOND_TICK); 00126 00127 this->reset_delay_secs = 0; 00128 } 00129 00130 void SimpleShell::on_second_tick(void *) 00131 { 00132 // we are timing out for the reset 00133 if (this->reset_delay_secs > 0) { 00134 if (--this->reset_delay_secs == 0) { 00135 system_reset(false); 00136 } 00137 } 00138 } 00139 00140 void SimpleShell::on_gcode_received(void *argument) 00141 { 00142 Gcode *gcode = static_cast<Gcode *>(argument); 00143 string args= get_arguments(gcode->command); 00144 00145 if (gcode->has_m) { 00146 if (gcode->m == 20) { // list sd card 00147 gcode->mark_as_taken(); 00148 gcode->stream->printf("Begin file list\r\n"); 00149 ls_command("/sd", gcode->stream); 00150 gcode->stream->printf("End file list\r\n"); 00151 00152 } else if (gcode->m == 30) { // remove file 00153 gcode->mark_as_taken(); 00154 rm_command("/sd/" + args, gcode->stream); 00155 00156 }else if(gcode->m == 501) { // load config override 00157 gcode->mark_as_taken(); 00158 if(args.empty()) { 00159 load_command("/sd/config-override", gcode->stream); 00160 }else{ 00161 load_command("/sd/config-override." + args, gcode->stream); 00162 } 00163 00164 }else if(gcode->m == 504) { // save to specific config override file 00165 gcode->mark_as_taken(); 00166 if(args.empty()) { 00167 save_command("/sd/config-override", gcode->stream); 00168 }else{ 00169 save_command("/sd/config-override." + args, gcode->stream); 00170 } 00171 } 00172 } 00173 } 00174 00175 bool SimpleShell::parse_command(unsigned short cs, string args, StreamOutput *stream) 00176 { 00177 for (ptentry_t *p = commands_table; p->pfunc != NULL; ++p) { 00178 if (cs == p->command_cs) { 00179 PFUNC fnc= p->pfunc; 00180 (this->*fnc)(args, stream); 00181 return true; 00182 } 00183 } 00184 00185 return false; 00186 } 00187 00188 // When a new line is received, check if it is a command, and if it is, act upon it 00189 void SimpleShell::on_console_line_received( void *argument ) 00190 { 00191 SerialMessage new_message = *static_cast<SerialMessage *>(argument); 00192 00193 // ignore comments 00194 if (new_message.message[0] == ';') return; 00195 00196 string possible_command = new_message.message; 00197 00198 //new_message.stream->printf("Received %s\r\n", possible_command.c_str()); 00199 00200 unsigned short check_sum = get_checksum( possible_command.substr(0, possible_command.find_first_of(" \r\n")) ); // todo: put this method somewhere more convenient 00201 00202 // find command and execute it 00203 parse_command(check_sum, get_arguments(possible_command), new_message.stream); 00204 } 00205 00206 // Act upon an ls command 00207 // Convert the first parameter into an absolute path, then list the files in that path 00208 void SimpleShell::ls_command( string parameters, StreamOutput *stream ) 00209 { 00210 string folder = absolute_from_relative( parameters ); 00211 DIR *d; 00212 struct dirent *p; 00213 d = opendir(folder.c_str()); 00214 if (d != NULL) { 00215 while ((p = readdir(d)) != NULL) { 00216 stream->printf("%s\r\n", lc(string(p->d_name)).c_str()); 00217 } 00218 closedir(d); 00219 } else { 00220 stream->printf("Could not open directory %s \r\n", folder.c_str()); 00221 } 00222 } 00223 00224 // Delete a file 00225 void SimpleShell::rm_command( string parameters, StreamOutput *stream ) 00226 { 00227 const char *fn= absolute_from_relative(shift_parameter( parameters )).c_str(); 00228 int s = remove(fn); 00229 if (s != 0) stream->printf("Could not delete %s \r\n", fn); 00230 } 00231 00232 // Change current absolute path to provided path 00233 void SimpleShell::cd_command( string parameters, StreamOutput *stream ) 00234 { 00235 string folder = absolute_from_relative( parameters ); 00236 00237 DIR *d; 00238 d = opendir(folder.c_str()); 00239 if (d == NULL) { 00240 stream->printf("Could not open directory %s \r\n", folder.c_str() ); 00241 } else { 00242 THEKERNEL->current_path = folder; 00243 closedir(d); 00244 } 00245 } 00246 00247 // Responds with the present working directory 00248 void SimpleShell::pwd_command( string parameters, StreamOutput *stream ) 00249 { 00250 stream->printf("%s\r\n", THEKERNEL->current_path.c_str()); 00251 } 00252 00253 // Output the contents of a file, first parameter is the filename, second is the limit ( in number of lines to output ) 00254 void SimpleShell::cat_command( string parameters, StreamOutput *stream ) 00255 { 00256 // Get parameters ( filename and line limit ) 00257 string filename = absolute_from_relative(shift_parameter( parameters )); 00258 string limit_paramater = shift_parameter( parameters ); 00259 int limit = -1; 00260 if ( limit_paramater != "" ) { 00261 char *e = NULL; 00262 limit = strtol(limit_paramater.c_str(), &e, 10); 00263 if (e <= limit_paramater.c_str()) 00264 limit = -1; 00265 } 00266 00267 // Open file 00268 FILE *lp = fopen(filename.c_str(), "r"); 00269 if (lp == NULL) { 00270 stream->printf("File not found: %s\r\n", filename.c_str()); 00271 return; 00272 } 00273 string buffer; 00274 int c; 00275 int newlines = 0; 00276 int linecnt= 0; 00277 // Print each line of the file 00278 while ((c = fgetc (lp)) != EOF) { 00279 buffer.append((char *)&c, 1); 00280 if ( char(c) == '\n' || ++linecnt > 80) { 00281 newlines++; 00282 stream->puts(buffer.c_str()); 00283 buffer.clear(); 00284 if(linecnt > 80) linecnt= 0; 00285 } 00286 if ( newlines == limit ) { 00287 break; 00288 } 00289 }; 00290 fclose(lp); 00291 00292 } 00293 00294 // loads the specified config-override file 00295 void SimpleShell::load_command( string parameters, StreamOutput *stream ) 00296 { 00297 // Get parameters ( filename ) 00298 string filename = absolute_from_relative(parameters); 00299 if(filename == "/") { 00300 filename = THEKERNEL->config_override_filename(); 00301 } 00302 00303 FILE *fp= fopen(filename.c_str(), "r"); 00304 if(fp != NULL) { 00305 char buf[132]; 00306 stream->printf("Loading config override file: %s...\n", filename.c_str()); 00307 while(fgets(buf, sizeof buf, fp) != NULL) { 00308 stream->printf(" %s", buf); 00309 if(buf[0] == ';') continue; // skip the comments 00310 struct SerialMessage message= {&(StreamOutput::NullStream), buf}; 00311 THEKERNEL->call_event(ON_CONSOLE_LINE_RECEIVED, &message); 00312 } 00313 stream->printf("config override file executed\n"); 00314 fclose(fp); 00315 00316 }else{ 00317 stream->printf("File not found: %s\n", filename.c_str()); 00318 } 00319 } 00320 00321 // saves the specified config-override file 00322 void SimpleShell::save_command( string parameters, StreamOutput *stream ) 00323 { 00324 // Get parameters ( filename ) 00325 string filename = absolute_from_relative(parameters); 00326 if(filename == "/") { 00327 filename = THEKERNEL->config_override_filename(); 00328 } 00329 00330 // replace stream with one that writes to config-override file 00331 FileStream *gs = new FileStream(filename.c_str()); 00332 if(!gs->is_open()) { 00333 stream->printf("Unable to open File %s for write\n", filename.c_str()); 00334 return; 00335 } 00336 00337 // issue a M500 which will store values in the file stream 00338 Gcode *gcode = new Gcode("M500", gs); 00339 THEKERNEL->call_event(ON_GCODE_RECEIVED, gcode ); 00340 delete gs; 00341 delete gcode; 00342 00343 stream->printf("Settings Stored to %s\r\n", filename.c_str()); 00344 } 00345 00346 // show free memory 00347 void SimpleShell::mem_command( string parameters, StreamOutput *stream) 00348 { 00349 bool verbose = shift_parameter( parameters ).find_first_of("Vv") != string::npos ; 00350 unsigned long heap = (unsigned long)_sbrk(0); 00351 unsigned long m = g_maximumHeapAddress - heap; 00352 stream->printf("Unused Heap: %lu bytes\r\n", m); 00353 00354 uint32_t f= heapWalk(stream, verbose); 00355 stream->printf("Total Free RAM: %lu bytes\r\n", m + f); 00356 00357 stream->printf("Free AHB0: %lu, AHB1: %lu\r\n", AHB0.free(), AHB1.free()); 00358 if (verbose) 00359 { 00360 AHB0.debug(stream); 00361 AHB1.debug(stream); 00362 } 00363 } 00364 00365 static uint32_t getDeviceType() 00366 { 00367 #define IAP_LOCATION 0x1FFF1FF1 00368 uint32_t command[1]; 00369 uint32_t result[5]; 00370 typedef void (*IAP)(uint32_t *, uint32_t *); 00371 IAP iap = (IAP) IAP_LOCATION; 00372 00373 __disable_irq(); 00374 00375 command[0] = 54; 00376 iap(command, result); 00377 00378 __enable_irq(); 00379 00380 return result[1]; 00381 } 00382 00383 // get network config 00384 void SimpleShell::net_command( string parameters, StreamOutput *stream) 00385 { 00386 void *returned_data; 00387 bool ok= THEKERNEL->public_data->get_value( network_checksum, get_ipconfig_checksum, &returned_data ); 00388 if(ok) { 00389 char *str= (char *)returned_data; 00390 stream->printf("%s\r\n", str); 00391 free(str); 00392 00393 }else{ 00394 stream->printf("No network detected\n"); 00395 } 00396 } 00397 00398 // print out build version 00399 void SimpleShell::version_command( string parameters, StreamOutput *stream) 00400 { 00401 Version vers; 00402 uint32_t dev = getDeviceType(); 00403 const char *mcu = (dev & 0x00100000) ? "LPC1769" : "LPC1768"; 00404 stream->printf("Build version: %s, Build date: %s, MCU: %s, System Clock: %ldMHz\r\n", vers.get_build(), vers.get_build_date(), mcu, SystemCoreClock / 1000000); 00405 } 00406 00407 // Reset the system 00408 void SimpleShell::reset_command( string parameters, StreamOutput *stream) 00409 { 00410 stream->printf("Smoothie out. Peace. Rebooting in 5 seconds...\r\n"); 00411 this->reset_delay_secs = 5; // reboot in 5 seconds 00412 } 00413 00414 // go into dfu boot mode 00415 void SimpleShell::dfu_command( string parameters, StreamOutput *stream) 00416 { 00417 stream->printf("Entering boot mode...\r\n"); 00418 system_reset(true); 00419 } 00420 00421 // Break out into the MRI debugging system 00422 void SimpleShell::break_command( string parameters, StreamOutput *stream) 00423 { 00424 stream->printf("Entering MRI debug mode...\r\n"); 00425 __debugbreak(); 00426 } 00427 00428 // used to test out the get public data events 00429 void SimpleShell::get_command( string parameters, StreamOutput *stream) 00430 { 00431 int what = get_checksum(shift_parameter( parameters )); 00432 void *returned_data; 00433 00434 if (what == get_temp_command_checksum) { 00435 string type = shift_parameter( parameters ); 00436 bool ok = THEKERNEL->public_data->get_value( temperature_control_checksum, get_checksum(type), current_temperature_checksum, &returned_data ); 00437 00438 if (ok) { 00439 struct pad_temperature temp = *static_cast<struct pad_temperature *>(returned_data); 00440 stream->printf("%s temp: %f/%f @%d\r\n", type.c_str(), temp.current_temperature, temp.target_temperature, temp.pwm); 00441 } else { 00442 stream->printf("%s is not a known temperature device\r\n", type.c_str()); 00443 } 00444 00445 } else if (what == get_pos_command_checksum) { 00446 bool ok = THEKERNEL->public_data->get_value( robot_checksum, current_position_checksum, &returned_data ); 00447 00448 if (ok) { 00449 float *pos = static_cast<float *>(returned_data); 00450 stream->printf("Position X: %f, Y: %f, Z: %f\r\n", pos[0], pos[1], pos[2]); 00451 00452 } else { 00453 stream->printf("get pos command failed\r\n"); 00454 } 00455 } 00456 } 00457 00458 // used to test out the get public data events 00459 void SimpleShell::set_temp_command( string parameters, StreamOutput *stream) 00460 { 00461 string type = shift_parameter( parameters ); 00462 string temp = shift_parameter( parameters ); 00463 float t = temp.empty() ? 0.0 : strtof(temp.c_str(), NULL); 00464 bool ok = THEKERNEL->public_data->set_value( temperature_control_checksum, get_checksum(type), &t ); 00465 00466 if (ok) { 00467 stream->printf("%s temp set to: %3.1f\r\n", type.c_str(), t); 00468 } else { 00469 stream->printf("%s is not a known temperature device\r\n", type.c_str()); 00470 } 00471 } 00472 00473 void SimpleShell::help_command( string parameters, StreamOutput *stream ) 00474 { 00475 stream->printf("Commands:\r\n"); 00476 stream->printf("version\r\n"); 00477 stream->printf("mem [-v]\r\n"); 00478 stream->printf("ls [folder]\r\n"); 00479 stream->printf("cd folder\r\n"); 00480 stream->printf("pwd\r\n"); 00481 stream->printf("cat file [limit]\r\n"); 00482 stream->printf("rm file\r\n"); 00483 stream->printf("play file [-v]\r\n"); 00484 stream->printf("progress - shows progress of current play\r\n"); 00485 stream->printf("abort - abort currently playing file\r\n"); 00486 stream->printf("reset - reset smoothie\r\n"); 00487 stream->printf("dfu - enter dfu boot loader\r\n"); 00488 stream->printf("break - break into debugger\r\n"); 00489 stream->printf("config-get [<configuration_source>] <configuration_setting>\r\n"); 00490 stream->printf("config-set [<configuration_source>] <configuration_setting> <value>\r\n"); 00491 stream->printf("config-load [<file_name>]\r\n"); 00492 stream->printf("get temp [bed|hotend]\r\n"); 00493 stream->printf("set_temp bed|hotend 185\r\n"); 00494 stream->printf("get pos\r\n"); 00495 stream->printf("net\r\n"); 00496 stream->printf("load [file] - loads a configuration override file from soecified name or config-override\r\n"); 00497 stream->printf("save [file] - saves a configuration override file as specified filename or as config-override\r\n"); 00498 } 00499
Generated on Tue Jul 12 2022 20:09:02 by 1.7.2