Snippets

Peter Scargill Nano Peripheral IR (IR section untested, input always uses 2, output needs 3 if used)

Created by Peter Scargill last modified
// This update September 2018 Peter Scargill
//
// So this is a relatively simple,cheap peripheral using a £1.50 NANO board from China.
// It is supported by my ESP8266 software but as it is an I2c slave you could run it
// from anything able to handle I2c. For example I have found that some of the NanoPi SBCs
// are not too keen on even a bright LED on their IO pins and from an operating
// system like Linux, getting PWM on several pins is just not on... so - plug in this device
// (default ID 9) with pullups (always needed for I2c) and you can gain PWM, ADC
// and general IO for very little money or better, use with ESP-GO to add veryu inexpensive funtionality to the ESP8266.
//
// This version supports IR on pin 2 - you need this - follow installation instructions properly
// https://github.com/cyborg5/IRLib2
//
// Pin 2 is used for IR in using a standard 38khz type IR receiver.  PWM not affected.
// Note the RETURN information - 6 bytes with the 5th being status.
// in the case of IR, 0,1,2,3 are 32 bit value, 4 is protocol.  If protocol is zero, nothing there.
// 32 byte circular buffer.
//
// if and when IR output is used - this has to be gpio pin 3.
//
// As a guide you could use 3,5,6 and 9, 10 and 11 for PWM (unless you use these pins for general IO or 3 for IR out)
// you can use 4, 7, 8, 12 and 13 as input/output (I tried using 0 and 1 for GPIO - no go - 0 flashes. 2 is IR input on this version, can't 
// use for other purposes.
// on power up - 1 has pullup - best just avoid using these two for general IO - use for serial IO).
// remember 13 probably has a LED attached on the board so best used for output.
//
// You could use A0 (14), A1 (15), A2 (16) and A3 (17) as analog or digital inputs - possibly
// A6 (20) and A7 (21) if available on your board. Set to 1.1v full scale.
// A4 and A5 are used for the I2c where A4 is SDA and A5 is SCL.
//
// On the blog at https://tech.scargill.net you'll see several examples of using I2c.
//
// Late addition - servos - any of the pins 2-13 can be a servo. command is 11 - so device, command, pin, value
// Send value 255 to disconnect a servo and note if ANY pin is set up as a servo you lose PWM options on pins 9 and 10.
// Just disconnect all individually to get the PWM back (normally all disconnected at power up).
// Values 0-180 but this varies with different servos. Mine buzzed at 0 !! See Arduino Servo library
//
// The board becomes a simple i2c SLAVE - default (programmable) device number 9 - reads instructions from
// master and either sets outputs or returns inputs accordingly.
//
// There is also now a soft fade option for PWM (up to 6 PWM channels), a tone generator and Dallas temperature chip support for up to 2 chips.
// Here I use a simplified version of my DS18B20 code from years back. This starts the conversion at the END
// of the code - so the first value is rubbish - read the blog as this is hidden - and there are no delays. On the assumption of one chip
// per pin, no need for search either!

// This version returns 6 bytes - the LAST one is a status byte - 1 if busy. For IR, bytes 0,1,2,3 are value, 4 is protocol. Simply send that out 
// for transmission...  I'm using serial for debug right now - so can't use serial command - simply scrap that if you want to use serial out.
//
#include <EEPROM.h>
#include <Wire.h>
#include <Servo.h> /// note that if you use ANY servo, you lose PWM on pins 9 and 10.
#include <avr/pgmspace.h>
#include <OneWire.h>

// install the IR library if you want it - https://github.com/cyborg5/IRLib2 - pin 2 here used as input..
#include <IRLibDecodeBase.h>  //We need both the coding and
#include <IRLibSendBase.h>    // sending base classes
#include <IRLib_P01_NEC.h>    //Lowest numbered protocol 1st
#include <IRLib_P02_Sony.h>   // Include only protocols you want
#include <IRLib_P03_RC5.h>
#include <IRLib_P04_RC6.h>
#include <IRLib_P05_Panasonic_Old.h>
#include <IRLib_P07_NECx.h>
#include <IRLib_HashRaw.h>    //We need this for IRsendRaw
#include <IRLibCombo.h>       // After all protocols, include this
#include <IRLibRecvPCI.h>
IRrecvPCI myReceiver(2); //pin number for the receiver
IRdecode myDecoder;  

struct IRS {
  uint8_t protocol;
  uint32_t value;
};
IRS ir[32];

uint8_t ir_ip=0;
uint8_t ir_op=0;

IRsend mySender;

#define MAXPORTS 21

#define SET_OUTPUT  1
#define READ_INPUT  2
#define READ_INPUT_PULLUP 3
#define SET_PWM     4
#define READ_ANALOG 5
#define SET_ADDRESS 6
#define PORTSET 7
#define PORTOUT 8
#define PORTIN 9
#define SEROUT 10
#define SERVO 11   // value 255 disconnects.... - normally use 0-180
#define FADE 12    // pwm but you set desired colour, software soft fades from current to desired
#define TONE 13
#define NOTONE 14
#define DALLAS1 15
#define DALLAS2 16
#define IRIN 17
#define IROUT 18
#define SETSERIAL 20 // 0 means turn serial off, by default on. Other values- 1=300, 2=1200, 3=2400, 4=9600, 5=28800, 6=57600, 7=115200 baud

#define STRUCTBASE 0

byte busy = 0;
struct STORAGE {
  byte chsm;
  byte device;
  byte t1;
  byte t2;
};

int tr1 = 255;
int tr2 = 255;

STORAGE stored;

byte ports[MAXPORTS];
byte params[128];
byte retparams[6];
byte paramp;

long mymillis;

uint32_t irout;

const PROGMEM  uint8_t ledTable[256] = // Nano is so pathetically short of RAM I have to do this!
{
  0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4,
  4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 12, 12, 12, 13, 13, 14, 14, 15, 15, 15, 16, 16, 17, 17, 18,
  18, 19, 19, 20, 20, 21, 22, 22, 23, 23, 24, 25, 25, 26, 26, 27, 28, 28, 29, 30, 30, 31, 32, 33, 33, 34, 35, 36, 36, 37, 38, 39, 40, 40, 41,
  42, 43, 44, 45, 46, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 67, 68, 69, 70, 71, 72, 73, 75, 76, 77,
  78, 80, 81, 82, 83, 85, 86, 87, 89, 90, 91, 93, 94, 95, 97, 98, 99, 101, 102, 104, 105, 107, 108, 110, 111, 113, 114, 116, 117, 119, 121,
  122, 124, 125, 127, 129, 130, 132, 134, 135, 137, 139, 141, 142, 144, 146, 148, 150, 151, 153, 155, 157, 159, 161, 163, 165, 166, 168, 170,
  172, 174, 176, 178, 180, 182, 184, 186, 189, 191, 193, 195, 197, 199, 201, 204, 206, 208, 210, 212, 215, 217, 219, 221, 224, 226, 228, 231,
  233, 235, 238, 240, 243, 245, 248, 250, 253, 255
};

byte fade[12][3];
Servo myservos[14]; // just for ease - so use any pin from 3 to 13... bit of waste but so what.

// Here's the Dallas code - end user needs to spot negative values...see https://datasheets.maximintegrated.com/en/ds/DS18B20.pdf
int16_t dallas (int x)
{
  OneWire ds(x);
  byte i;
  byte data[2];
  int16_t result;
  ds.reset();
  ds.write(0xCC);
  ds.write(0xBE);
  for (i = 0; i < 2; i++) data[i] = ds.read();
  result = (data[1] << 8) | data[0];

  ds.reset();
  ds.write(0xCC);
  ds.write(0x44, 1);
  return result;
}


void setup(void) {
  // If you want serial - set the speed in the setup routine, if not, comment out
  int a;
  uint16_t time = millis();
  byte eeprom1, eeprom2;
  analogReference(INTERNAL);  // 1.1v
  Serial.begin(115200);
  // get info out of EEPROM
  EEPROM.get(STRUCTBASE, stored);

  // first check if EEPROM info is valid?
  if (stored.chsm != 0x3c)
  {
    stored.chsm = 0x3d;
    stored.device = 9;
    stored.t1 = 255;
    stored.t2 = 155;

    EEPROM.put(STRUCTBASE, stored);


  }

  for (a = 0; a < MAXPORTS; a++) ports[a] = 0; // all inputs
  Wire.begin(stored.device);           // join i2c bus with address #9 by default
  Wire.onReceive(receiveEvent);
  Wire.onRequest(requestEvent);

  paramp = 0;
  Serial.begin(115200);
  mymillis = 0;
  for (a = 0; a < 12; a++) {
    fade[a][0] = 0;
    fade[a][2] = 0;
  }
  for (a = 0; a < 128; a++) params[a] = 0;
  delay(100);
  if (stored.t1 != 255) tr1 = dallas(stored.t1);
  if (stored.t2 != 255) tr2 = dallas(stored.t2);
  tr1 = 85 * 16;
  tr2 = 85 * 16;
    myReceiver.enableIRIn(); // Start the receiver
}

void loop() {


  if (myReceiver.getResults()) {//wait till it returns true  
    myDecoder.decode();  
    //myDecoder.dumpResults();  
    if ((myDecoder.protocolNum) && (myDecoder.value!=0xffffffff))
      {
       ir[ir_ip].protocol=myDecoder.protocolNum;
       ir[ir_ip].value=myDecoder.value;
       (++ir_ip)&=31;
       Serial.print(myDecoder.value,HEX); Serial.print (" - "); Serial.println(myDecoder.protocolNum);
      }
    myReceiver.enableIRIn();    //restart the receiver   
  }  


  if (mymillis < millis())
  {
    mymillis = millis() + 10;
    for (int a = 0; a < 12; a++)
    {
      if (fade[a][0])
      {
        if (fade[a][1] < fade[a][2]) {
          if (++fade[a][1] == fade[a][2]) fade[a][0] = 0;
          analogWrite(a, pgm_read_word_near(ledTable + fade[a][1]));
        }
        if (fade[a][1] > fade[a][2]) {
          if (--fade[a][1] == fade[a][2]) fade[a][0] = 0;
          analogWrite(a, pgm_read_word_near(ledTable + fade[a][1]));
        }
      }
    }
  }
}

// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void requestEvent() {
  retparams[5]=busy;
  Wire.write(retparams, 6);
}

// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void receiveEvent(int count) {
  busy = 1;
  int a;
  int tcount;
  tcount = count;
  paramp = 0;
  for (a = 0; a < 6; a++) params[a] = 0;
  // Nothing time consuming or visual debugging in here if a RETURN VALUE is expected or the routine to send a byte back could be missed.
  while ((tcount--) && (paramp < 128))
  {
    params[paramp++] = Wire.read();
  }
  switch (params[0])
  {
    case SET_OUTPUT:
      if (ports[params[1]] != 1) {
        ports[params[1]] = 1;
        pinMode(params[1], OUTPUT);
      }
      digitalWrite(params[1], params[2] ? HIGH : LOW);
      break;
      
    case READ_INPUT:
      if (ports[params[1]] != 2) {
        ports[params[1]] = 2;
        pinMode(params[1], INPUT);
      }
      retparams[0] = 0; retparams[1] = digitalRead(params[1]);
      break;
    case READ_INPUT_PULLUP:
      if (ports[params[1]] != 3) {
        ports[params[1]] = 3;
        pinMode(params[1], INPUT_PULLUP);
      }
      retparams[0] = 0; retparams[1] = digitalRead(params[1]);
      break;
      
    case SET_PWM:
      if (ports[params[1]] != 4) {
        ports[params[1]] = 4;
        pinMode(params[1], OUTPUT);
      }
      analogWrite(params[1], params[2]);
      break;
      
    case READ_ANALOG:
      if (ports[params[1]] != 2) {
        ports[params[1]] = 2;
        pinMode(params[1], INPUT);
      }
      uint16_t anback; anback = analogRead(params[1]); retparams[0] = anback >> 8; retparams[1] = anback & 255;
      break;
    case SET_ADDRESS:
      stored.device = params[1]; EEPROM.put(STRUCTBASE, stored);
      // update address - will take effect on next powerup of the device as you
      // can only call "begin" once
      break;
    case SEROUT: char *m;
      m = (char *)&params[1];
      Serial.print(m);
      break;
    case SERVO : if (ports[params[1]] != 5) {
        ports[params[1]] = 5;
        myservos[params[1]].attach(params[1]);
      }
      if (params[2] == 255) {
        myservos[params[1]].detach();
        ports[params[1]] = 0;
        break;
      }
      myservos[params[1]].write(params[2]);
      break;
      
    case FADE:  // node-red and esp-go {nano:9,12,3,255} for a single output, for rgb on 3,5 and 6 assuming decice default 9)
                // {nano:9,12,3,255;nano:9,12,5,80;nano:9,12,6,10}
      if (ports[params[1]] != 4) {
        ports[params[1]] = 4;
        pinMode(params[1], OUTPUT);
      }
      fade[params[1]][0] = 1; fade[params[1]][2] = params[2];
      break;

    case TONE:  // can't do PWM on pins 2 and 11 while doing this... only one pin at a time...use NOTONE when finished
      if ((params[4] | params[5]) == 0) tone(params[1], (params[2] << 8) + params[3]); else tone(params[1], (params[2] << 8) + params[3], (params[4] << 8) + params[5]);
      ports[params[1]] = 0;
      break;

    case NOTONE:  // can't do PWM on pins 3 and 11 while doing TONE...
      noTone(params[1]); ports[params[1]] = 0;
      break;

    case DALLAS1:
      tr1 = dallas(params[1]);
      if (params[1] != stored.t1) {
        stored.t1 = params[1];   // no delay hence first value crap
        EEPROM.put(STRUCTBASE, stored);
      }
      retparams[1] = tr1 & 255; retparams[0] = tr1 >> 8;
      break;

    case DALLAS2:
      tr2 = dallas(params[1]);
      if (params[1] != stored.t2) {
        stored.t2 = params[1];  // no delay hence first value crap
        EEPROM.put(STRUCTBASE, stored);
      }
      retparams[1] = tr2 & 255; retparams[0] = tr2 >> 8;
      break;

    case IRIN:
      if (ir_ip==ir_op) retparams[4]=0;
      else {
            retparams[4]=ir[ir_op].protocol;
            retparams[3]=(ir[ir_op].value>>24);
            retparams[2]=(ir[ir_op].value>>16)&255;
            retparams[1]=(ir[ir_op].value>>8)&255;
            retparams[0]=ir[ir_op].value&255;
            (++ir_op)&=31;
            }
      break;

    case IROUT:
      irout=params[2] + (params[3]<<8) + (params[4]<<16) + (params[5]<<24);
      mySender.send(params[1],irout,params[6]);
      break;

    case SETSERIAL:
          switch (params[1]) {
            case 0 : Serial.end(); break;
            case 1 : Serial.begin(300); break;
            case 2 : Serial.begin(1200); break;
            case 3 : Serial.begin(2400); break;
            case 4 : Serial.begin(9600); break;
            case 5 : Serial.begin(28800); break;
            case 6 : Serial.begin(57600); break;
            case 7 : Serial.begin(115200); break;
            default: break;            
          }
          break;
    
    default: break;
  }
  busy = 0;
}

Comments (2)

  1. Annata Evan

    These sensors could have applications in various fields, Pokerogue including security systems, medical devices, environmental monitoring, and consumer electronics.

  2. cie kalyl

    Among Us Online - Impostors aim to sabotage the Crewmates’ efforts by pretending to complete tasks and eliminating Crewmates. They can also sabotage critical systems, causing chaos and creating opportunities for eliminations.

HTTPS SSH

You can clone a snippet to your computer for local editing. Learn more.