Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
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
