Now that I have moved house from the Arduino IDE into Code::Blocks, I'm going to document the start of the development process in Code::Blocks. I expect to be tripped up by a number of problems and I'll write down how I muddle through.
I have my ConsoleDateTime project for Code::Blocks set up. I deliberately did not pull my DateTime.h and DateTime.cpp files over from Arduino. I'm going to run the two IDEs side-by-side and pull across chunks of code one at a time and deal with any errors that come up as I go.
Get code here: https://github.com/prawnhead/ConsoleDateTime
At the moment I have a bare DateTime class and want to populate it with private attributes. I need a number of ints and bytes to store the year, month, day, etcetera. And here I strike the first hurdle. When I create a private variable in the DateTime class such as this:
private:
byte year;
... and try to compile in Code::Blocks, I get an error:
||=== ConsoleDateTime, Debug ===|
include\DateTime.h|14|error: 'byte' does not name a type|
||=== Build finished: 1 errors, 0 warnings (0 minutes, 0 seconds) ===|
It seems byte is not a defined type outside the Arduino environment. Well, I know C++ is a bare set of tools from which just about anything can be created. The Arduino environment wants an 8-bit (probably unsigned) data type called byte, so it must be defined by the Arduino environment somehow. This is why I have Notepad++ on my quick-list of applications. Fire it up, hit Ctrl-Alt-F to do a Find in Files, point it towards my Arduino folder and have the Filters set to: *.h; *.cpp, *.ino, *.c, then search for "#define byte" (without the quotes, of course). I get a hit in the GSM library, GSM3CircularBuffer.h, that says:
#define byte uint8_t
That's cool, but it's probably not the "main" definition of the byte data type. I search again just on "byte" and get 2042 hits. Oops. That's not helpful. Oh, maybe it's a typedef, not a #define. Typedef statements are the other way around, so we're looking for typedef uint8_t byte; assuming uint8_t found above is the type that it uses - uint8_t means "unsigned integer of 8 bits type". The search finds line 94 of Arduino.h. Jackpot. This must be the "main" definition of the byte type.
typedef uint8_t byte;
So I slapped in the typedef for the byte type at the top of my DateTime.h file and put it inside the existing #ifndef ARDUINO area. So this code will be invoked by Code::Blocks but not by Arduino since it already has the type defined. The project now compiles and runs. So now I can use the byte type. I created the following private attributes for the DateTime class:
byte _year, _month, _day, _hour, _minute, _second;
unsigned int _millisecond;
I'm a little unsure about _year. Maybe having a range of only 256 years is too short-sighted. Then again, having a two-byte data type to store the milliseconds makes each DateTime object eight bytes instead of six. Hmm. Ponderous. On to the next issue!
I want to add a toString() function next. I add a public function signature for string toString(); but it won't compile. Code::Blocks turns the word string green. What's the colour coding I wonder? I try a few things, #include <string>, using uppercase String and so on without luck. Come on Google, save me. OK, I was almost there. String is part of the standard namespace (std), so I added another line into my #ifndef ARDUINO area for using namespace std; Now I smell a rat. A dead one. I'm fairly sure the string class I've used in Arduino is String (capital S), the one in standard C++ is string (lowercase s). This looks like it will break when I try to run this in the Arduino environment. But the toString() function test works in Code::Blocks just fine so far (code here: https://github.com/prawnhead/ConsoleDateTime/commit/86cb9fabb3d45cddabfb9a722357eec99fef950a)
Now I'm going to do a "drop-in test" for Arduino. As I did in my last post, I'm just going to copy the DateTime.h and DateTime.cpp files from ConsoleDateTime into a dummy Arduino project (TestDateTime) and see if it will compile and run. Here goes ...
In file included from DateTime.cpp:1:
DateTime.h:15: error: 'string' does not name a type
DateTime.h:18: error: 'byte' does not name a type
DateTime.cpp: In constructor 'DateTime::DateTime()':
DateTime.cpp:13: error: 'Serial' was not declared in this scope
DateTime.cpp: In destructor 'virtual DateTime::~DateTime()':
DateTime.cpp:22: error: 'Serial' was not declared in this scope
DateTime.cpp: At global scope:
DateTime.cpp:26: error: 'string' does not name a type
As expected. An interesting allotment of error messages. The first odd thing I see is the byte type is not working. Ah, I didn't #include <Arduino.h>. Now I need a #else region in my DateTime.h file. The list of errors falls down to two when I change the top of my header file to this:
#ifndef DATETIME_H
#define DATETIME_H
#ifndef ARDUINO
#include <iostream>
#include <string>
typedef uint8_t byte;
using namespace std;
#else
#include <Arduino.h>
#endif
Now I just have the errors about 'string' does not name a type. But I already know the string type in Arduino is named differently, probably so there isn't confusion between the C++ "string" type and the Arduino (Processing) String type. So how do I account for the difference? Typedef to the rescue again. I'm just going to use Arduino style String variables, but make sure in Code::Blocks the string type is substituted for the String type at compile type. Of course this is on the assumption that both the C++ string type and the Processing String type implement the same interface - we'll see! Now the top of my DateTime.h file looks like this, and I've changed the toString() function to return a String type:
#ifndef DATETIME_H
#define DATETIME_H
#ifndef ARDUINO
#include <iostream>
#include <string>
using namespace std;
typedef uint8_t byte;
typedef string String;
#else
#include <Arduino.h>
#endif
Redoing my drop-in test for Arduino I can now compile and run the same DateTime files in both environments. That includes downloading and running on the Arduino microcontroller itself. Great start! Code at this stage: https://github.com/prawnhead/ConsoleDateTime/commit/012210ce0041fe019fb082e899d1728d030e767e
The next issue I see coming is that I've started to heavily use the F() function in Arduino to store strings in flash memory. See a previous post on Using Program Memory (PROGMEM) for details. Obviously Code::Blocks won't know what to do with this so I want a macro that will just remove it.
I'm not really familiar with macros but saw a lot of them when I was researching DateTime, so I'll dig back through that post and find the code examples I saw. Ah, here it is. By the looks of it I can just do this:
#define F(X) X
"When you see function F(), strip it out!" Let's see if that works. Of course we only want this macro to strip out the F() function in standard C++ (Code::Blocks) and not in Arduino. It does work! At the moment it looks odd that I've used the macro to remove the F() function from code that's not running in Arduino anyway, but I'm sure there will come a time when I've got the F() function in code and I want it taken care of for both environments. Code at this stage: https://github.com/prawnhead/ConsoleDateTime/commit/17c70d7ce744ba0028e8391567eef51ec469ecd3
No I have no idea what to tackle next. That's ok, the bigger picture was to start pulling code from the Arduino DateTime library into the Code::Blocks one and fix issues as I go. I'm going to carry on with that and not slow myself down by blogging the whole thing. I'll do regular commits on the code so you can step through the process if you care to.
Hopefully you see from this and my previous one post how to break out of the Arduino IDE when you don't need it. You can then develop in a much faster and more feature-rich environment. Whether you use Code::Blocks or another integrated development environment (IDE) is up to you. I hope I've done Code::Blocks justice in these posts - I kinda like it!
That is all.
Comments
Post a Comment