IO Expansion methods

Do you have a question on how to do something.
Ask in here.

Posts: 32
Joined: Thu Nov 24, 2016 7:19 am
PostPosted: Sat Jan 07, 2017 5:34 pm
I just got my Water Level and IO Expansion and Expansion hub yesterday. Got the WL all calibrated and it is working GREAT! Should have done this long ago. I do daily water changes and by using the high and low ATO memvars I can dial in EXACTLY how much water to drain before turning the ATO back on. Also, I noticed it had come on alot over night and realized that i had the upper limit set above may overflow drain and so the RO was running ever 30-45 min. i was able to keep dialing it down while i was out to running errands this morning until I got it below the overflow mark. This was all done on my phone!!!! Its freakin cool!!!!

This evening I have turned my attention to start using the IO and have run into a bit of a snafu.....

I have scoured the forum, the code (even stepping through every instance of "IO" in ReefAngle.ccp) and google for info on how to use the IO Expansion. The only thing i have found is this rather old reference which appears to be from when it was in its infancy.


The IO class itself only has one method - GetChannel which appears to only do a read. That would be the "Output" part of IO, but how do you write to it? Also, how do you address each individual pin on the device?

A couple of code snippets should be able to get me on my way.


Code: Select all
#include <ReefAngel_Features.h>
#include <Globals.h>
#include <RA_Wifi.h>
#include <Wire.h>
#include <OneWire.h>
#include <Time.h>
#include <DS1307RTC.h>
#include <InternalEEPROM.h>
#include <RA_NokiaLCD.h>
#include <RA_ATO.h>
#include <RA_Joystick.h>
#include <LED.h>
#include <RA_TempSensor.h>
#include <Relay.h>
#include <RA_PWM.h>
#include <Timer.h>
#include <Memory.h>
#include <InternalEEPROM.h>
#include <RA_Colors.h>
#include <RA_CustomColors.h>
#include <Salinity.h>
#include <RF.h>
#include <IO.h>
#include <ORP.h>
#include <AI.h>
#include <PH.h>
#include <WaterLevel.h>
#include <Humidity.h>
#include <DCPump.h>
#include <PAR.h>
#include <ReefAngel.h>

////// Place global variable code below here
// Define Custom Memory Locations
#define Mem_B_MoonOffset      100
#define Mem_B_Vacation        101
#define Mem_B_AutoFeed        102
#define Mem_B_AutoFeedPress   103
#define Mem_B_AutoFeedRepeat  104
#define Mem_B_AutoFeedOffset  105
#define Mem_I_WCFillTime      106

byte wc_status = 0;
time_t ato_endfill;
byte ato_topping = 0;

void init_memory() {
    // Initialize Custom Memory Locations
    InternalMemory.write_int(Mem_I_WCFillTime,1200  );  //How long topoff is allowed to run

// Define Relay Ports by Name
#define T5Light            1
#define LEDLights          2
#define ATODrain           3
#define Feeder             4
#define Mixer              5
#define Topoff             6
#define Ferts              7
#define Filter             8

////// Place global variable code above here

void setup()
    // This must be the first line
    ReefAngel.Init();  //Initialize controller
    ReefAngel.Use2014Screen();  // Let's use 2014 Screen
    ReefAngel.AddWaterLevelExpansion();  // Water Level Expansion Module
    // Ports toggled in Feeding Mode
    ReefAngel.FeedingModePorts = 0;
    // Ports toggled in Water Change Mode
    ReefAngel.WaterChangePorts = 0;
    // Ports toggled when Lights On / Off menu entry selected
    ReefAngel.LightsOnPorts = 0;
    // Ports turned off when Overheat temperature exceeded
    ReefAngel.OverheatShutoffPorts = 0;
    // Use T1 probe as temperature and overheat functions
    ReefAngel.TempProbe = T1_PROBE;

    // Ports that are always on
    ////// Place additional initialization code below here

    // Ports that default off

    ReefAngel.CustomLabels[0]="T5 Light";

////// Place additional initialization code above here

void loop()
    ReefAngel.StandardLights( LEDLights);
    ReefAngel.StandardLights( T5Light);
    ReefAngel.PWM.SetActinic( PWMSlope(9,0,20,0,15,100,60,15) );
    ////// Place your custom code below here


    //Water Change
    if (hour() == InternalMemory.DP1Timer_read() && minute() == InternalMemory.DP1RepeatInterval_read()) {
        wc_status = 1;

    if (wc_status > 0 ) {

    //Keep Water topped off

    ////// Place your custom code above here

    // This should always be the last line
    ReefAngel.Portal( "diverjoe" );

void AutoTopOff() {
//run for 30 sec any time float valve is turned on and WC drain is not active

    if (ato_endfill > now() && wc_status != 1 && ato_topping) {
    else {
        ato_topping = 0;
        if ( ReefAngel.WaterLevel.GetLevel() < InternalMemory.WaterLevelLow_read  () && !ato_topping) {
            ato_endfill = now() + InternalMemory.ATOExtendedTimeout_read();
            ato_topping = 1;

void RunFeeder() {
    boolean autoFeed;
    static time_t t;
    int press, repeat, offset;;;*SECS_PER_HOUR;*SECS_PER_HOUR;

    ReefAngel.Relay.Set(Feeder, autoFeed);
    if (ReefAngel.Relay.Status(Feeder)!=autoFeed) {
        InternalMemory.write(Mem_B_AutoFeed, autoFeed);

    if (autoFeed) {
        if (((hour() > 9 && hour() < 20)) && (now()+offset)%repeat==0) {

    if (ReefAngel.Relay.isMaskOn(Feeder)) {
        t=now()-now()%20; // One feeding has already happened.

    // Press the button once every 20 seconds
    if (now()-t < press*20) {
    } else {

void AutoWaterChange() {
    int NowMins;
    static time_t t;

    if (wc_status == 1) {
    if(ReefAngel.WaterLevel.GetLevel() < InternalMemory.WaterLevelHigh_read() && wc_status == 1)
        wc_status = 2;   //Enter Mix mode

        t = now();

    //Ferts Mode
    if ( wc_status == 2) {
        if (now() - t > SECS_PER_MIN  * InternalMemory.DP2Timer_read()) {
            wc_status = 3;   //enter Ferts mode
            t = now();

    //ferts Mode
    if ( wc_status == 3) {
        if (now() - t > InternalMemory.DP2RepeatInterval_read()) {
            wc_status = 0;


// Disable masks for things that should not be turned on by mistake
void LockPorts() {


Posts: 12116
Joined: Fri Mar 18, 2011 6:47 pm
PostPosted: Sat Jan 07, 2017 7:24 pm
Are you trying to use it as output or input?

Posts: 32
Joined: Thu Nov 24, 2016 7:19 am
PostPosted: Sat Jan 07, 2017 7:39 pm
I will use some as input only and some as outputs

Posts: 12116
Joined: Fri Mar 18, 2011 6:47 pm
PostPosted: Sat Jan 07, 2017 10:57 pm
As input, it is easy.
Code: Select all
if (ReefAngel.IO.GetChannel(0))

As output, we don't have a library for it.
The reason is that it is not recommended unless you really know what you doing and understand a little of electronics.
Each channel is only capable of driving 25mA. So, you can't really drive anything more than a small LED.
So, you are better off using one of the relays for output rather than the I/O.

Posts: 32
Joined: Thu Nov 24, 2016 7:19 am
PostPosted: Sun Jan 08, 2017 8:41 am
i am not an expert, but i do know the basics about electronics. I will mostly be driving a relay for my 5v/12v stuff.

Posts: 12116
Joined: Fri Mar 18, 2011 6:47 pm
PostPosted: Sun Jan 08, 2017 10:38 am
Ok. good.
So, just make sure you do not drive the relay directly from the pin of the I/O module.
You need a relay driver with a feedback diode.
Something like this: ... ircuit.php

Posts: 32
Joined: Thu Nov 24, 2016 7:19 am
PostPosted: Sun Jan 08, 2017 12:16 pm
yea, i have an opto isolated relay like the sainsmart i had from a netduino project.

Posts: 12116
Joined: Fri Mar 18, 2011 6:47 pm
PostPosted: Sun Jan 08, 2017 2:08 pm
Ok, so you are good.
You can update the I/O module just like the relay box, except you change the address to the I/O expansion module.
It uses the same chipset PCF8574.
Code: Select all

Where data is what you want to output.

Posts: 32
Joined: Thu Nov 24, 2016 7:19 am
PostPosted: Sun Jan 08, 2017 2:40 pm
Wow that is plenty easy enough. But one question. Which of the 6 will that data be sent to?


Posts: 32
Joined: Thu Nov 24, 2016 7:19 am
PostPosted: Sun Jan 08, 2017 8:45 pm
[quote="rimai"]As input, it is easy.
Code: Select all
if (ReefAngel.IO.GetChannel(0))

After learning how I2C works and seeing how it is
used then reviewing the schematic I have come to
Understand that the is a I2C slave based off the 328p.
With that in mind I looked back at all the code libraries
Especially the expansion libraries. I am not seeing how
I can get more that a bit from the IO expansion. Will it
Return more than 1 byte as in the io library? I can only
assume that there is some code running on the 328p that
"Packages" the data from the 6 pins into 1 byte.

Please help me understand how to talk to this thing.

Return to How do I code ...

Who is online

Users browsing this forum: No registered users and 1 guest