Sunday, August 13, 2017

Atom - platformio = fail

Editing both the main sketch and the libraries was a total hassle in the Arduino IDE. After a quick look around I installed atom and platformio. Very sexy.

UPDATE: platformio is a complete fail. I was getting unusual data from the DS3231 clock, but I couldn't tell if it was my wrapper or the original library that I'd grabbed for the Arduino IDE.

Long story short, I tried renaming the original library folder, installed the first DS3231 library from the platformio gui (after I hunted the gui down) and got the same result.

Ok, no big deal, let's see what the library is doing with the hour/h12/pm data.

Wait, where is the library? It's not in the lib directory. I guess it's global?

So where is the global library folder? It's not visible in the project folder/file list on the left.

I hunt down the library gui button that has the 'reveal in finder' button, and resort to opening the .cpp file in.... TextWrangler. What a waste of time. I can't edit the .cpp file in platformio? I can't select to open it with Atom - platformio just throws a big red error box and pukes.

I'm giving up on platformio for now; it's library system is overly complicated/obtuse/opaque, and I can't easily open my own global library files to edit them at the same as my source code. It is in active development so I can see it getting better, but for now there isn't any difference to just opening everything in TextWrangler and recompiling in the Arduino IDE.

It seems to work; after some futzing around with extra #includes in the projects main .cpp file  I think I foiled the broken library path logic enough - I have a few custom libs and it's compiling without throwing errors because it can't find the Arduino base libs like EEPROM.h and my hacked Wire.h.

It's notion of statically compiled libraries in a per-project /lib folder is totally counterintuitive to why I write libraries in the first place - shared, reusable code. Also, there were a few support questions and the response was 'our libs are better'. Really? Better than what the entire open source community writes? Bullshit.

But whatever, it works for now. Worst case is the author updates it, breaks the logic in a different way, and then I throw it all out and go back to the Arduino IDE, TextWrangler, and forcing recompiles outside by deleting .o files. Blech.

Saturday, August 12, 2017

Status snapshot 11-Aug-17

Library updates / integration list:

LibraryexternMinimum FunctionsGeneral purpose notes
timestamp.h (DS3231 RTC)
get time, make timestamp...
getRTC() current time>reg[]
setAL1(), setAL2() set alarms from reg[]
setRTC() to update clock
i2c access to 24LC256 chip
//rem'd out
read(), write() single bytes
readBlock, writeBlock to copy config / comm areas in one call
read(), write() single bytes, from any memory media
extend to include SDFAT
prepare a 16 byte block of log data, either performance or status
read(), inits, 
Both angle/target seeking and basic chip control
getPos(), setDrive(), setTarget() (has az,el angle conversions as well
MCP (MCP 23017)PartialNo defined interface yet
LCDPendingNo defined interface yet
avr (base)PendingGeneric A/D read/write
ESP8266PendingWill need to incorporate
Extend to include SPIFFS comm buffer (eventually)
BattBoxPendingNeed simple avr i2c slave to read some adc lines for current, voltage

// TODO:
  • Write up something that manages the 4 byte structs for describing execution state. Library?Plain array? Library, I think. This is linked to schedule, code and the way it's loaded in memory as objects. Gotta finish that this weekend.
  • copy the sun position schedule currently in a FLASH_ARRAY into some of the unused low bytes of internal EEPROM. I think it fits between the reg[] and MAP.
  • sift thru the current spreadsheet of opcodes and stuff all the blocking calls into one group so we can flag those in the sequence generator - in case we need to run those as 'exclusive'.
  • I had an idea to wrap the stock LCD library so we can include references to a string_table for option-list type fields. Currently it's all numeric. Smells like scope creep.
  • UPDATE: I forgot the August 31 deadline for autonomous operations. I've started a reverse calendar to count down the work items left. It's about 2 weeks of time left, and about 6 weeks of work
  • sun sensor - sealing the top and mounting the base to the array.
  • fabric enclosure completion (it's held on with bungee cords right now)
  • user interface 'box' mounting and cable routing, mounting the panels and power switches
  • power and comms cable from the battery box
  • revisit vmem - make it buffer-backed for 32 byte page-writes.


  • array mounting and charge cabling
  • platform, table, and mast build, linear actuator tests, azimuth feedback testing and characterization
  • sensor build and testing
  • 3D printing the feedback rack and gear for elevation
  • 3D printing the feedback gear for the XL belt azimuth table
  • 3D printing the sub-panels for the user interface box
  • write up the keywords.txt and example for the XEEPROM library, and test it all out
  • organize the reg[ ] array into config and run entries. Those entries that are only used during setup() don't have to be copied in memory. I should be able to save about 32 bytes of SRAM this way.

Friday, August 11, 2017

Variables and Parameters

The first thing I pulled into the main code was the real time clock / timestamp library, and it turned out to be a good choice because it was non-trivial. Most classes have private and public members, but in the case of 'time', it needs to be accessible to other libraries, like logging and scheduling.

One way is to simply call rtcupdate() to fetch valid entries for a time struct, then add a parameter for it in the function calls. In memory constrained situations this isn't ideal. The stack is only so large and garbage collection is seldom merciful (or timely).

Early on I decided I prefer to create a register array of bytes for my 'globals'. This means I can look down a list of #defines and see which globals I have and how they are organized. It also means that I don't create a lot of near-duplicates (or outright duplicates), pass a lot of useless stuff on the stack, and generally keep things tidy. This is really important on the ATTINY 85 (and 84), but also handy on the 328's. It also means that turning a project into an I2C slave is trivial, since the registers already exist.

The implementation when mixed with several libraries is less obvious. Class files aren't aware of globals; they don't 'see' the uint8_t reg[64] declaration. There are two ways around this; pointers and extern. Both work, but the way I did it, pointers was just ever so slightly clunkier than using extern as I had to pass the address of the global reg[] array in the class .begin() call.

This allows for the following basic rules:

  1. class / private variables: never seen outside the class implementation. Can use get/set syntax for access
  2. class / public variables: never needed outside of either the class or loop(); preferred if no get/set syntax is helpful for housekeeping
  3. shared globals via extern: used to make global scope variables visible to class implementations.
To simplify point 3 even more, the only variable I plan on using this way is the reg[] array. We'll see if that holds up.


I should probably document how this works... after looking online I see that there is a lot of incomplete examples out there.

main (where setup and loop are):

#include "registers.h"
uint8_t reg[ REG_SIZE ];

void loop() {
  Serial.print( reg [ DT_MINUTE ] );
  Serial.print( " - ");
  Serial.println( reg [ DT_SECOND ] );

 delay(1000); // for demo, doesn't use RTC for timing


// fragment only:
#define REG_SIZE 64

#define DT_YEAR   16
#define DT_MONTH  17
#define DT_DATE   18
#define DT_HOUR   19
#define DT_MINUTE 20
#define DT_SECOND 21

Now the library (there is only a reference in the .cpp file, not in the .h):

timestamp.cpp (uses ds3231.h lib for actual i2c comms):

#include "registers.h"
extern uint8_t reg[  ];

void timestamp::getRTC( void ) {
 // fragment
  reg[ DT_MINUTE ] = Clock.getMinute();
  reg[ DT_SECOND ] = Clock.getSecond();