Logging either on the fly or post-deployment of the A.Bot
JonPeyton
Posts: 10
I'm wanting to try and log the maneuvers that I put my Activity Bot through.
Having finished the corresponding tutorial I guess I need to use the drive_getTicks function but I am unsure how to implement this in a way that will retrieve data created while the bot is "un-tethered" so to speak from the USB.
This may not be possible or this maybe an oversight of the obvious by me, please forgive either way....
thank you!
JP
Having finished the corresponding tutorial I guess I need to use the drive_getTicks function but I am unsure how to implement this in a way that will retrieve data created while the bot is "un-tethered" so to speak from the USB.
This may not be possible or this maybe an oversight of the obvious by me, please forgive either way....
thank you!
JP

Comments
forums.parallax.com/discussion/comment/1332769/#Comment_1332769
One problem is that when I wrote this, the libraries included the function "itoa()". That function converted an integer into text, but was removed from the newer directories.
By saving the text representation to the SD card and separating values by "," or CR, the numbers can easily be imported into other programs.
The sprint() or sprinti() function would have to be substituted for itoa() where ever it is called. The parameters for the itoa and sprint functions are different.
atoi parameters are integer to convert, character string to hold converted digits, number base of the integer.
int n = sprinti(buffer, "%d", x); // convert number to string, n is returned and is the length of the string, buffer is a character array long enough to hold the integer digits, x is the integer, %d is the formatting which converts the decimal (base 10) representation of x. sprinti only works with integers and saves significant code space.
hope this helps
Tom
Here also is an EEPROM example that stores navigation values between each maneuver.
#include "simpletools.h" #include "abdrive.h" int left, right, address; int main() { address = 32768 - 4; print("DATA DURING TRAVEL\n\n"); drive_goto(256, 256); pause(200); drive_getTicks(&left, &right); print("left = %d, right = %d\n", left, right); ee_putInt(left, address += 4); ee_putInt(right, address += 4); drive_goto(26, -25); pause(200); drive_getTicks(&left, &right); print("left = %d, right = %d\n", left, right); ee_putInt(left, address += 4); ee_putInt(right, address += 4); drive_goto(100, 100); pause(200); drive_getTicks(&left, &right); print("left = %d, right = %d\n", left, right); ee_putInt(left, address += 4); ee_putInt(right, address += 4); print("\nDATA THAT WAS LOGGED:\n\n"); int endAddress = address; for(address = 32768; address < endAddress; address += 8) { left = ee_getInt(address); right = ee_getInt(address + 4); print("left = %d, right = %d\n", left, right); } }Let's say that right now, your code drives the Activity Bot to a certain place, then logs the number of ticks to the terminal. Simple. That program might look like this if you're using the Simple libraries (as Andy showed above)
#include "simpletools.h" #include "abdrive.h" int main() { int left; int right; drive_goto(256, 256); pause(200); drive_getTicks(&left, &right); print("left = %d, right = %d\n", left, right); }If you want to use PropWare, you don't have to change much
#include <PropWare/printer/printer.h> #include "abdrive.h" int main() { int left; int right; drive_goto(256, 256); pause(200); drive_getTicks(&left, &right); pwOut.printf("left = %d, right = %d\n", left, right); }From here, I'm going to add a line at the top that seems rather useless.... but you'll understand in a bit.
#include <PropWare/printer/printer.h> #include "abdrive.h" using namespace PropWare; int main() { Printer logger = pwOut; int left; int right; drive_goto(256, 256); pause(200); drive_getTicks(&left, &right); logger.printf("left = %d, right = %d\n", left, right); }And now you can modify this to save to an SD card by adding a few lines to the top (to initialize your SD card) and then change a single word at the bottom:
#include <PropWare/printer/printer.h> #include <PropWare/filesystem/fat/fatfilewriter.h> #include <PropWare/filesystem/sd.h> #include "abdrive.h" using namespace PropWare; int main() { const SD driver; FatFS filesystem(&driver); filesystem.mount(); FatFileWriter writer(filesystem, "motorLog.csv"); Printer filePrinter(&writer); Printer logger = filePrinter; int left; int right; drive_goto(256, 256); pause(200); drive_getTicks(&left, &right); logger.printf("left = %d, right = %d\n", left, right); }Now, you might think "wow! that's a lot of code at the top now!" And in this small example, it is a lot of lines. However, realize that after the line "Printer logger = filePrinter", there were no code changes. The entire rest of your program does not need to change. How cool is that?!
Thank you both so very much!
I cant wait to get home from work and try out the solutions you presented me with!
JP
I haven't used Prop ware (or much C++) yet but I'm not afraid
Thank you for this , I find myself in a lot of head scratching situations at my desk, the more tools I have to free myself the better!
Ill let you know how it goes David.
JP
All 3 approaches (eventually) worked fine.The only reason for the delay was my inexperience with some of the terminology involved in swapping the functions out and being sure to declare and initialize everything properly.properly. I've a lot to learn clearly Tom!
Andy I loaded the code straight away into EEPROM and ran it.The bot did the maneuvers, but where I became lost was I realized that this was something I would view through the terminal so therefore I needed to be attached to the USB right? (I built a stand for my bot that allows me to run code that spins the wheels while still on the desk) this is how I ran it the second time.Did I misunderstand? Is there a way to play out the maneuvers un-tethered and store the data in the on-board memory then come back and have it displayed in the terminal? I'm sorry for my nievete and thank you for your patience! Regardless its still a clean and entirely readable way to log the moves perhaps before actually performing them
David looking at Propware everything seems to make sense (within the limited understanding I have as a whole about this stuff).I did the Propware for Windows.Its in C:\Propware.{BTW I'm Win 8.1 pro 32g RAM i7 4790K}
I installed and configured everything correctly I believe.Perhaps I copied the code wrong as I kept getting this when I tried to load it:
propWare.cpp:9:9: error: 'SD' does not name a type
propWare.cpp:10:25: error: 'driver' was not declared in this scope
propWare.cpp:13:24: error: 'printer' was not declared in this scope
I just copied the code straight from the reply you sent yesterday.
Please advise if you think I'm not setup correctly as I very much would like to add Propware to my arsenal!
Thanks fellas,
and now off to work
JonPeyton
Okay, your comments make me realize that I have not provided any kind of guidance for users which would help them decide between the "Windows" and "SimpleIDE" instructions that I provide. I've created an issue for this and will update the docs over the weekend.
In the meantime, I'll simply tell you that you're going to want to use the "SimpleIDE" instructions. You can skip step 1 of those instructions since you already have that available under C:\PropWare.
The other instructions are all intended for someone that wants to use PropWare's build system which is an alternative to SimpleIDE. I only recommend PropWare's build system if you are already familiar with the command line or you would prefer to use another IDE such as CLion or Eclipse.
Although I've been able to build PropWare projects from the command line, building from SimpleIDE has not been as successful. I've followed the SimpleIDE instructions for PropWare, downloading the "libraries & header files" (I copied those into /Learn/Simple Libraries/), and get the same results as Jon. So, looks like there's either a missing step or some configuration problem (BTW: I reset my environment to export $PropWarePath to point towards the newly downloaded PropWare directory, which I place in /Learn/Simple Libraries/). Not sure if that would affect SimpleIDE builds but did it as an extra measure :-)
dgately
#include <PropWare/printer/printer.h> #include <PropWare/filesystem/sd.h> #include <PropWare/filesystem/fat/fatfilewriter.h> #include "abdrive.h" using namespace PropWare; int main() { const SD driver; FatFS filesystem(&driver); filesystem.mount(); FatFileWriter writer(filesystem, "motorLog.csv"); Printer filePrinter(&writer); Printer logger = filePrinter; int left; int right; drive_goto(256, 256); pause(200); drive_getTicks(&left, &right); logger.printf("left = %d, right = %d\n", left, right); }This did overflow when compiled with LMM, so be sure you're using CMM for your memory model.
Use Load EEPROM & Run with the example below. Then, turn off power, unplug, and take it somewhere to navigate. Turn on power, if the P26 light turns on, let it do its navigation. If P27 turns on, press/release RST within 2 seconds, then P26 should turn on in another second. With P26 on, let do its navigation.
When the navigation has completed (and logged), turn off power and bring to computer and plug in. Click Program and select Open Terminal. If the Enable/Disable button displays Enable, click it. If it displays Disable, leave it. Then, turn bot power on. If the light is P27, it'll show the logged data. If the light is P26, you'll have 2 seconds to press/release RST to make P27 come on and display the data.
#include "simpletools.h" #include "abdrive.h" #define EE_MODE 32768 #define EE_LAST 32768 + 4 #define EE_DATA 32768 + 8 void logManeuvers(void); void displayLog(void); int ee_storeTicks(int left, int right, int address); int ee_getTicks(int *left, int *right, int address); int left, right; int main() { int address = EE_MODE; int mode = ee_getInt(address); ee_putInt(mode + 1, address); if( (mode % 2) == 1) { high(26); pause(2000); logManeuvers(); } else { high(27); pause(2000); displayLog(); } } void logManeuvers(void) { int address = EE_DATA; print("DATA DURING TRAVEL\n\n"); drive_goto(256, 256); pause(200); drive_getTicks(&left, &right); print("left = %d, right = %d\n", left, right); address = ee_storeTicks(left, right, address); drive_goto(26, -25); pause(200); drive_getTicks(&left, &right); print("left = %d, right = %d\n", left, right); address = ee_storeTicks(left, right, address); drive_goto(100, 100); pause(200); drive_getTicks(&left, &right); print("left = %d, right = %d\n", left, right); address = ee_storeTicks(left, right, address); print("\nDone! Press/release RST to view data.\n\n"); } void displayLog(void) { print("\nDATA THAT WAS LOGGED:\n\n"); int endAddress = ee_getInt(EE_LAST); int address = EE_DATA; while(address < endAddress) { address = ee_getTicks(&left, &right, address); print("left = %d, right = %d\n", left, right); } } int ee_storeTicks(int left, int right, int address) { //print("ee_storeTicks(%d, %d, %d) = ", left, right, address); ee_putInt(left, address); ee_putInt(right, address += 4); address += 4; ee_putInt(address, EE_LAST); //print("%d\n", address); return address; } int ee_getTicks(int *left, int *right, int address) { *left = ee_getInt(address); *right = ee_getInt(address += 4); address += 4; //print("ee_getTicks(%d, %d, %d) = ", *left, *right, address-8); //print("%d\n", address); ee_putInt(address, EE_LAST); return address; }Thanks!
dgately
@JonPeyton and Andy:
I've written up an EEPROM class for PropWare that uses the same interfaces (ScanCapable and PrintCapable) as the serial (UART) and SD card (FileWriter) classes. This will allow you to easily switch between terminal, eeprom, and SD card
eeprom.h
#pragma once #include <PropWare/i2c.h> #include <PropWare/printcapable.h> #include <PropWare/scancapable.h> namespace PropWare { /** * @brief EEPROM reader/writer */ class Eeprom: public PrintCapable, public ScanCapable { public: /** First byte above the standard 32kB EEPROM */ static const uint16_t DEFAULT_INITIAL_MEMORY_ADDRESS = 32 * 1024; /** Standard EEPROM I2C address used for Propeller microcontrollers */ static const uint8_t DEFAULT_DEVICE_ADDRESS = 0x50 << 1; public: /** * @brief Construct an instance that, by default, will read from and write to the standard EEPROM * * @param[in] driver I2C bus driver * @param[in] initialMemoryAddress Byte of EEPROM that should be read from or written to first when using * methods that do not accept an address as a parameter * @param[in] deviceAddress EEPROM address on the I2C bus (for instance, the default device * address would be passed in as `0x50 << 1`, _not_ `0x50`) * @param[in] autoIncrement Should the memory address pointer be automatically incremented upon * reading and writing bytes from and to the EEPROM, similar to * reading/writing files */ Eeprom(const I2C &driver = pwI2c, const uint16_t initialMemoryAddress = DEFAULT_INITIAL_MEMORY_ADDRESS, const uint8_t deviceAddress = DEFAULT_DEVICE_ADDRESS, const bool autoIncrement = true) : m_driver(&driver), m_memoryAddress(initialMemoryAddress), m_deviceAddress(deviceAddress), m_autoIncrement(autoIncrement) { } /** * @brief Check that the EEPROM is responding * * @returns True if the EEPROM is responding, false otherwise */ bool ping() const { return this->m_driver->ping(this->m_deviceAddress); } /** * @brief Place a byte of data into a specific address * * @param[in] address Address in EEPROM to place data * @param[in] byte Data to be written to EEPROM * * @returns True if the data was successfully written, false otherwise */ bool put(const uint16_t address, const uint8_t byte) const { // Wait for any current operation to finish while (!this->ping()); return this->m_driver->put(this->m_deviceAddress, address, byte); } /** * @brief Place multiple bytes of data into sequential memory locations in EEPROM * * @param[in] startAddress Address to store the first byte of data * @param[in] bytes[] Array of data - no null-terminator necessary * @param[in] length Number of bytes in the array that should be sent * * @returns True if the data was successfully written, false otherwise */ bool put(const uint16_t startAddress, const uint8_t bytes[], const size_t length) const { // Wait for any current operation to finish while (!this->ping()); return this->m_driver->put(this->m_deviceAddress, startAddress, bytes, length); } /** * @see PropWare::PrintCapable::put_char * * @post Internal memory address pointer will be incremented */ virtual void put_char(const char c) { this->put(this->m_memoryAddress, (uint8_t) c); if (this->m_autoIncrement) ++this->m_memoryAddress; } /** * @see PropWare::PrintCapable::puts * * @post Internal memory address pointer will be incremented by the length of the string */ virtual void puts(const char *string) { const size_t stringLength = strlen(string); this->put(this->m_memoryAddress, (const uint8_t *) string, stringLength); if (this->m_autoIncrement) this->m_memoryAddress += stringLength; } /** * @brief Read a byte from EEPROM * * @param[in] address Address in EEPROM to be read * * @returns Data in EEPROM */ uint8_t get(const uint16_t address) const { // Wait for any current operation to finish while (!this->ping()); return this->m_driver->get(this->m_deviceAddress, address); } /** * @see PropWare::PrintCapable::get_char * * @post Internal memory address pointer will be incremented */ virtual char get_char() { const uint8_t byte = this->get(this->m_memoryAddress); if (this->m_autoIncrement) ++this->m_memoryAddress; return byte; } /** * @brief Retrieve the current address of the internal pointer * * @returns Address in EEPROM used for reading or writing the next byte */ uint16_t get_memory_address() const { return this->m_memoryAddress; } /** * @brief Set the current address of the internal pointer * * @param[in] address Address in EEPROM used for reading or writing the next byte */ void set_memory_address(const uint16_t address) { this->m_memoryAddress = address; } /** * @brief Determine if auto incrementing of the internal address pointer is enabled * * @returns True if enabled, false otherwise */ bool is_auto_increment() const { return m_autoIncrement; } /** * @brief Set auto incrementing of the internal address pointer to enabled or disabled * * @param[in] autoIncrement True to enabled, false to disable */ void set_auto_increment(const bool autoIncrement) { m_autoIncrement = autoIncrement; } private: const I2C *m_driver; uint16_t m_memoryAddress; const uint8_t m_deviceAddress; bool m_autoIncrement; }; }PropWareEeprom_Demo.cpp
#include <PropWare/printer/printer.h> #include <PropWare/eeprom.h> #include <PropWare/scanner.h> const uint8_t MAGIC_ARRAY_1[] = "DCBA0"; const size_t ARRAY_SIZE_1 = sizeof(MAGIC_ARRAY_1); const char MAGIC_ARRAY_2[] = "Hello, world!"; const size_t ARRAY_SIZE_2 = sizeof(MAGIC_ARRAY_2); const uint16_t TEST_ADDRESS = 32 * 1024; // Place the data immediately above the first 32k of data /** * @example PropWareEeprom_Demo.cpp * * Read from and write to the EEPROM that comes with your Propeller * * @include PropWare_Eeprom/CMakeLists.txt */ int main() { PropWare::Eeprom eeprom; //////////////////////////////////////////////////////////////////////////////////////////////// // Here we have some core access of the EEPROM, passing it the address with every call. This is great for non-ASCII // data, but it gets tedious. pwOut << "EEPROM ack = " << eeprom.ping() << '\n'; bool success = eeprom.put(TEST_ADDRESS, MAGIC_ARRAY_1, ARRAY_SIZE_1); pwOut << "Put status: " << success << '\n'; pwOut << "Received character: " << (char) eeprom.get(TEST_ADDRESS) << '\n'; pwOut << "Received character: " << (char) eeprom.get(TEST_ADDRESS + 1) << '\n'; pwOut << "Received character: " << (char) eeprom.get(TEST_ADDRESS + 2) << '\n'; pwOut << "Received character: " << (char) eeprom.get(TEST_ADDRESS + 3) << '\n'; pwOut << "Received character: " << (char) eeprom.get(TEST_ADDRESS + 4) << '\n'; //////////////////////////////////////////////////////////////////////////////////////////////// // This is great for ASCII data, because we have access to the Printer and Scanner classes. pwOut << "Notice that PropWare::Eeprom also implements the PropWare::PrintCapable \n" "and PropWare::ScanCapable interfaces. So we could also use the Propware::Printer \n" "and PropWare::Scanner objects for reading and writing.\n"; PropWare::Printer eepromPrinter(&eeprom); PropWare::Scanner eepromScanner(&eeprom); // Reset the EEPROM address eeprom.set_memory_address(TEST_ADDRESS); // Note that the newline is required, or else the scanner won't know when to stop reading eepromPrinter << MAGIC_ARRAY_2 << '\n'; char buffer[ARRAY_SIZE_2]; // Reset the EEPROM address so that we can read what we just wrote eeprom.set_memory_address(TEST_ADDRESS); // Read from the EEPROM eepromScanner.gets(buffer, ARRAY_SIZE_2); pwOut << "Received \"" << buffer << "\" from the EEPROM!\n"; return 0; }-- Edit --
Documentation finished; Source code updated. I'll post a link to the source on GitHub once merged with the develop branch