nanoI2CSlaveExpander
ARDUINO Nano v3 used as I2C slace digital and analog io expander for ESP8266 and others...
nanoI2CIOExpander.ino
Go to the documentation of this file.
1 /**
2 * @file nanoI2CIOExpander.ino
3 @mainpage
4 * @author J.Soranzo
5 * @date December 2018
6 * @copyright 2018 CC0
7 * @version see config.h
8 * @brief ARDUINO Nano as I2C analog and digital IO expander
9 
10 @section licence
11 
12 This software is under CC0
13 
14 https://creativecommons.org/publicdomain/zero/1.0/legalcode.fr
15 
16 @section Documentation
17 
18 Github project page : https://github.com/MajorLee95/nanoI2CIOExpander
19 
20 Give you additionnal 8 digital in/out and 6 analog inputs
21 
22 with a simple low cost ARDUINO nano
23 
24 more with a MEAG2560 but not tested and not implemented
25 
26 Very useful for ESP8266 with its only one analog input
27 
28 I2C base address is in the config.h and completed by D12..D10 pin with internal pullup
29 
30 Analog A4 and A5 on nano are reserved for I2C communications.
31 
32 @section Exemples
33 
34 Connect SCLK to SCLk on ESP8266 and SDATA to SDATA on ESP8266.
35 
36 Don't forget to connect ground between the components.
37 
38 @section dependencies Lib dependencies
39 
40  Wire version 1.0
41 
42 * @todo Add MEGA2560 more fonctionnalities (more analog and digital IO)
43 
44 */
45 
46 #include <Wire.h>
47 #include "debugSerialPort.h"
48 #include "config.h"
49 
50 /**
51 * @var registers
52 * @brief Main registers bank. See config.h MAXREG macro for full mapping;
53 */
55 
56 /**
57 @fn void setup()
58 @brief ARDUINO setup function
59 @return no return value and no parameters
60 
61 Prepare i2c slave address
62 
63 Set debug port speed if DEBUG is enabled in the config.handler
64 
65 Display Startup screen with build i2c add
66 
67 Register 2 i2c event functions
68 
69 Initialize registers
70 
71 Display initialized registers
72 */
73 void setup(){
74 
75 
76  DEBUGPORT.begin( DEBUGSPEED );
77 
78  DEFDPROMPT( "IOExpander setup" );
79  //display Startup message
80  DSP( dPrompt + String(ARDUINO_TYPE) + F(" version : ") + MA_VERSION +"."+MI_VERSION );
81  DSP(F(" : BUILD "));
82  DSPL( (String)__DATE__ + " " + (String)__TIME__ );
83  int address = i2cBuildAddress();
84  DSPL( dPrompt + F("I2C adresse : ")+String(address, HEX) );
85  Wire.begin( address );
86  Wire.onReceive(receiveEvent); // register event
87  Wire.onRequest(requestEvent); // register event
88 
89  regInit();
90  registers[3] = 0x55;
91  for ( int i = 0; i < MAXREG ; i++ ){
92  DSPL( dPrompt + "registers[" + String(i,HEX) + "] = " + String( registers[i], HEX) );
93  }
94 
95 }
96 
97 int octetsRec = 0; /**< @brief number of i2C received bytes*/
98 int reg = 1;/**< @brief global variable to track the registers address for i2c requests*/
99 
100 /**
101 @fn void loop()
102 @brief ARDUINO loop, cyclicly update analog registers and digital
103 @return no return and no input parameter
104 
105 The cyclical update of the registers is not very fast.
106 We could do this in a better way
107 
108 * @todo improve the speed by updating i/o in a better way.
109 
110 */
111 void loop(){
112  DEFDPROMPT("Receiver");
113  delay(100);
114  if (octetsRec != 0){
115  DSPL( dPrompt + "Bytes rec : " + (String)octetsRec );
116  DSPL( dPrompt + "reg = " + String(reg, HEX));
117  DSPL( dPrompt + "registers[reg] = " + String(registers[reg], HEX) );
118  if ( reg == 8 ){
119  DSPL( "( (registers[5] & (1 << i) ) >> i ) avec i = 1 vaut " + (String)( (registers[5] & (1 << 1) ) >> 1 ) );
120  }
121  octetsRec = 0;
122  }
123  updateDigitals();
124  updateAnalogs();
125 }
126 /**
127 @fn void receiveEvent(int howMany)
128 @brief i2c receiver handler
129 @param howMany : nomber of received bytes
130 (dosen't work if there is a Serial.print in the handler)
131 @return nothing
132 */
133 void receiveEvent(int howMany) {
134  octetsRec = howMany;
135  int data;
136  // Si howMany = 2 => écriture;
137  // Si howMany = 1 => lecture;
138  // while (1 < Wire.available()) { // loop through all but the last
139  // char c = Wire.read(); // receive byte as a character
140  // Serial.print(c); // print the character
141  // }
142  reg = Wire.read();
143  /**
144  * @todo check for wrong parameters number
145  */
146  if (howMany == 2) {
147  data = Wire.read();
148  switch ( reg ){ //write enabled registers
149  case 1:
150  case 2:
151  case 4:
152  case 5:
153  case 7:
154  case 8:
155  case 9:
156  case 10:
157  registers[ reg ] = data;
158  break;
159  }
160  }
161 }
162 
163 /**
164 @fn void requestEvent()
165 @brief I2C transmit handler for Read request
166 */
167 void requestEvent() {
168  Wire.write(registers[reg]); // respond with message of 6 bytes
169  // as expected by master
170 }
171 
172 /**
173 @fn void regInit()
174 @brief initialisation of the register bank
175 
176 Works on global registers array.
177 */
178 void regInit(){
179 
180  //first init all to 0xA5
181  for ( int i = 0; i < MAXREG ; i++ ) registers[i] = 0xA5;
182 
183  //individual special registers init;
184  registers[0] = MA_VERSION;
185  registers[6] = MI_VERSION;
186  registers[1] = 0;
187  registers[2] = 0;
188  registers[3] = 0x55;
189  registers[4] = 0;
190  registers[7] = 0;
191  registers[8] = 0;
192  registers[9] = 0;
193  registers[10] = 0;
194  registers[24] = 0xCA;
195  registers[25] = 0xFE;
196  registers[26] = 0xFE;
197  registers[27] = 0xCA;
198 }
199 
200 /**
201 @fn int i2cBuildAddress()
202 @brief Build i2c slave address with base ored with D12..D10 pin state
203 @return the builded address
204 */
206  int address = I2CADD ;
207  //v2 chnage
208  // pinMode( I2CHIGHADD, INPUT_PULLUP );
209  // pinMode( I2CMIDADD, INPUT_PULLUP );
210  DEFDPROMPT("I2C ADD builder");
211 
212  pinMode( I2CLOWADD, INPUT_PULLUP );
213  DSPL( dPrompt + "add pin number = " + String(I2CLOWADD) );
214  // address |= digitalRead( I2CHIGHADD ) << 2;
215  // address |= digitalRead( I2CMIDADD ) << 1;
216  DSPL( dPrompt + "D13 = " + String(digitalRead( I2CLOWADD )?"HIGH":"LOW") );
217  address |= digitalRead( I2CLOWADD );
218  return address;
219 }
220 /**
221 @fn void updateAnalogs()
222 @brief as its name tells us update analog registers with analog inputs values
223 @return nothing and no input parameters
224 
225 Works on global registers arrays
226 */
228  int data;
229  for ( int i = 0; i < 8 ; i++ ){
230  if ( i != 4 && i !=5 ){
231  data = analogRead( 14+i ); //14 <=> A0
232  registers[0x10 + i*2] = byte(data & 0xFF);
233  registers[0x10 + i*2+1] = byte((data & 0xFF00) >> 8);
234  }
235  }
236 
237 }
238 
239 /**
240 @fn void updateDigitals()
241 @brief update digital registers and digital outputs
242 @return nothing and no input parameters
243 
244 Works on global registers arrays
245 */
247  byte directions = registers[4];
248  byte pullups = registers[7];
249 
250  for ( int i = 0; i < 8 ; i++ ){
251  if ( directions & (1 << i) ){ //output
252  pinMode( i+2, OUTPUT );
253  digitalWrite( i+2, ( ( registers[5] & (1 << i) ) >> i ) );
254  } else {
255  int mode = (pullups & (1 << i) )?INPUT_PULLUP:INPUT;
256  pinMode( i+2, mode );
257  bitWrite( registers[5], i, digitalRead( i+2) );
258  }
259  }
260 
261  //v2.0 add 3 digital i/O on D10, D11, D12
262  directions = registers[8];
263  //data are on registers[9]
264  pullups = registers[10];
265  for ( int i = 0; i < 3 ; i++ ){
266  if ( directions & (1 << i) ){ //output
267  pinMode( i+10, OUTPUT );
268  digitalWrite( i+10, ( ( registers[9] & (1 << i) ) >> i ) );
269  } else {
270  int mode = (pullups & (1 << i) )?INPUT_PULLUP:INPUT;
271  pinMode( i+10, mode );
272  bitWrite( registers[9], i, digitalRead( i+10) );
273  }
274  }
275 }
#define ARDUINO_TYPE
Only for debug display.
Definition: config.h:80
void requestEvent()
I2C transmit handler for Read request.
nanoI2CIOexpander porject configuration file
void setup()
ARDUINO setup function.
#define MAXREG
Maximum number of register.
Definition: config.h:56
void updateDigitals()
update digital registers and digital outputs
void regInit()
initialisation of the register bank
#define DEBUGPORT
#define I2CLOWADD
Definition: config.h:73
#define MI_VERSION
Minor version.
Definition: config.h:21
#define DSP(X)
#define I2CADD
I2C base address to be completed by D12..D10 pin states.
Definition: config.h:69
void receiveEvent(int howMany)
i2c receiver handler
int i2cBuildAddress()
Build i2c slave address with base ored with D12..D10 pin state.
int reg
global variable to track the registers address for i2c requests
byte registers[MAXREG]
Main registers bank. See config.h MAXREG macro for full mapping;.
all macros that i use tu print debug message on ARUDINO
#define DEFDPROMPT(X)
int octetsRec
number of i2C received bytes
#define DSPL(X)
#define MA_VERSION
project version not file version
Definition: config.h:20
#define DEBUGSPEED
void updateAnalogs()
as its name tells us update analog registers with analog inputs values
void loop()
ARDUINO loop, cyclicly update analog registers and digital.