Skip to content
  • MySensors
  • OpenHardware.io
  • Categories
  • Recent
  • Tags
  • Popular
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Brand Logo
rvendrameR

rvendrame

@rvendrame
Hero Member
About
Posts
353
Topics
5
Shares
0
Groups
1
Followers
0
Following
4

Posts

Recent Best Controversial

  • 3.3 or 5v tranformer for mysensors projects
    rvendrameR rvendrame

    @Tmaster , we have discussed the LNK302 chip a while ago, it may be interesting to check: https://forum.mysensors.org/search?term=lnk302&in=titlesposts

    I did POC here, and it does work, but I was a bit afraid of non-isolation so I decided to use the Hi-Link, assuming it would be more safe (and less components to assembly). I'm running a couple of nodes with Hi-Link for ~2 years without a glitch.

    The classic transformer approach for sure brings more isolation and safety. Also it is a good choice if you want to detect zero-cross (for AC dimmers) and measure real power consumption (by reading the real AC voltage from the transformer's AC output). On the other hand I see the bigger footprint as well as more components to assembly.

    Just my two cents...

    Hardware

  • Transformer-less power supply
    rvendrameR rvendrame

    Despite the security (which of course is the most important aspect here), such kind of circuit usually consumes from 2 to 10w (depending on AC voltage and C1 value), no matter in standby or not. I did some testing in the past and at end I dropped in favor of hi-link and similar small PSUs.

    My Project

  • Safe In-Wall AC to DC Transformers??
    rvendrameR rvendrame

    The review of the hlktech's HLK-PM01 is available here!

    Hardware

  • Transformer-less power supply
    rvendrameR rvendrame

    You can find "Hi-link like" PSUs at Aliexpress for less than $2 USD. Not sure how less you can reach by producing them by yourself.

    My Project

  • Sensebender Micro
    rvendrameR rvendrame

    Batteries usually provide a 'clean' power supply. The capacitor is more useful when there is ripple from the AC adapter.

    Announcements

  • Difficulties compiling Nodemcu 1.0 (ESP-12 module) as a 8266 Gateway.
    rvendrameR rvendrame

    @ramwal Thanks for the hint, it made the trick here too!

    Troubleshooting

  • Multi Button Relay switch
    rvendrameR rvendrame

    @jeylites you can also try adding some delay between each gw.send or gw.present. In my case it did the difference.

    Hardware

  • Trying to do a 3 dimmer Led controller with motion
    rvendrameR rvendrame

    By using built-in PWM you are limited to 3 outputs (3, 5 and 6), as pins 9, 10 and 11 are being used to connect to nRF radio).

    To read the rotaries you need at least two pins for each ( = "2-bit gray code"). Most rotaries have a 3rd microswitch, and work as a push-button as well, so a extra pin will allow you to have a on-off switch by pushing (instead turning) the rotary .

    Below is my sketch with 3 dimmers (using timers). If you implement timers, you can use any pin. (and it also works with AC-Triacs --- Be careful!!!) . If you are dimming AC bulbs, you need to adjust the 'freqStep' variable according to the AC frequency of your region.

     int freqStep = 84;                   // Microseconds of each tic  =  1s / 60Hz * 100 steps 
    

    I also recommend a 16Mhz Arduino, so you have enough speed to achieve good fade effects in all 3 channels while not missing any radio message.

    Dimmer 1 & 2 can be controlled by rotaries connected on (A3,A4) and (A5,A6). I also use A0 and A1 to immediate on/off by pushing each rotary. Dimmer 3 can only be increased/decreased via Incoming message (although it has a local on/off switch on pin A2).

    Hope it helps!

    /*** 
     * 
     * 3x Bulb/Led dimmer
     * 
     ***/
    
    #include <TimerOne.h> 
    #include <MySensor.h> 
    #include <SPI.h>
    #include <IRLib.h>
    
    #define SN "theDimmer"
    #define SV "1.3"
    
    // Version history
    // 1.2 4-Apr-15: Include "fade to level" logic
    // 1.3 11-Apr-15:  Changed A0-A6 input pins order; code cleanup
    
    // MySensor gateway, dimmer & light messages
    MySensor gw; 
    MyMessage msg1[3];
    MyMessage msg2[3]; 
    
    // Infrared Send module 
    IRsend irsend;
    
    // Warning message
    MyMessage var1( 0 , V_VAR1); 
    volatile boolean AC_found = false; 
    
    // Dim Level 
    int level[3] ;    // Current dim level on OUTPUT pins (0 to 100)
    int level_gw[3];  // Current dim level at Controller 
    int level_tg[3];  // Current dim level (target)
    float gap; 
    
    // Output pins (to Triacs / MOSFETs)
    int out_pin[3] = { 5, 6, 7 };  // Output PINS
    
    // Input: local rotary encoders (only 2) - 2-bit gray code
    int a,b,oldA[2],oldB[2];
    int rotary[2][2] = {  { A3 , A4 }  ,  { A5, A6 } };  // INPUT PINS
    
    // Input: local switches
    boolean newSw, oldSw[3]; 
    long lastSw[3];  
    int sw[3] = { A0, A1, A2 };  // INPUT PINS 
    
    // Infrared Emiiter Pin
    #define IR_PIN 3
    unsigned long raw[400];   // raw sequence of IR codes
    
    // Clock ticker (100 tics = 1 AC wave cycle = 1s / 120Hz = 8333 milisecs
    volatile int counter = 100;          // Timer tick counter
    int freqStep = 84;                   // Microseconds of each tic  =  1s / 60Hz * 100 steps 
    volatile boolean zero_cross = false; // Zero-cross flag
    int zero_cross_pin = 2;             // Input PIN for zero-cross detection 
    volatile int int_ct = 0 ; 
    
    // Idle counter (for gw update); 
    boolean idle; 
    int idle_ct; 
    
    void setup() { 
    
      // Serial init
      Serial.begin(115200); 
      
      // Start radio Controller
      gw.begin( incomingMessage );
      gw.sendSketchInfo( SN, SV );
    
      // IR output available? 
      pinMode( IR_PIN, INPUT );
      delay(50); 
      if ( digitalRead( IR_PIN ) == LOW ) { 
        pinMode( IR_PIN , OUTPUT );   
        gw.present( 4 , S_IR ); 
        Serial.println("IR init"); 
      } 
    
      // Setup IN/OUT pins
      for (int i = 0; i < 3; i++ ) { 
    
        // levels / step
        level[i] = 0;
        level_gw[i]=0;
        level_tg[i]=0; 
        
        // Create return messages
        msg1[i] = MyMessage( i , V_DIMMER ) ; 
        msg2[i] = MyMessage( i , V_LIGHT ) ; 
    
        // Rotary Encoders
        if ( i < 2) { 
          pinMode( rotary[i][0] , INPUT_PULLUP );
          pinMode( rotary[i][1] , INPUT_PULLUP ); 
          oldA[i] = digitalRead( rotary[i][0] );
          oldB[i] = digitalRead( rotary[i][1] );
        } 
    
        // Input Switches/PBs
        pinMode( sw[i] , INPUT_PULLUP );
        oldSw[i] = digitalRead( sw[i] ); 
        lastSw[i] = millis(); 
    
        // Triac/MOSFET out
        pinMode( out_pin[i], INPUT ); 
        delay(50);
        if ( digitalRead( out_pin[i] ) == LOW ) { 
    
          pinMode( out_pin[i] , OUTPUT ); 
          digitalWrite( out_pin[i], LOW );  
    
          // Register Dimmable Lights with the gateway
          gw.present( i, S_DIMMER );
         
          // Fetchs previous state from controller
          gw.request( i , V_DIMMER ); 
    
          Serial.print("Dimmer init CH "); 
          Serial.println( i );  
    
        } 
    
      } 
    
      // Zero-cross detection on Pin 2 
      pinMode( zero_cross_pin , INPUT_PULLUP);
      attachInterrupt( 0, zero_cross_detect, RISING );  
    
      // Clock ticker
      Timer1.initialize(); 
      Timer1.attachInterrupt( dim_check , freqStep ); 
    
    } 
    
    // Zero-cross
    void zero_cross_detect() { 
      
      zero_cross = true;
      AC_found = true; 
      counter = 100; 
      for (int i=0; i<3; i++)  digitalWrite( out_pin[i], LOW ) ; 
    
    } 
    
    // Timer Ticker - check
    void dim_check() { 
    
      int j = 0; 
    
      if ( zero_cross == true ) { 
    
        for (int i=0; i<3; i++)  
          if ( counter <= level[i] ) { 
            digitalWrite( out_pin[i] , HIGH ); 
            j++;
          } 
       
    
        // If all 3 output fires, avoid further checks... 
        if ( j == 3) zero_cross = false; 
      } 
    
      // Tic-tac...
      counter--; 
    
      // No zero-cross circuit?  As contingency, emulate it based on timer 
      // (Should work at least for PWM led running by  MOSFET )
      if ( counter == 0 ) { 
    
        zero_cross = true;
        counter = 100; 
        for (int i=0; i<3; i++)  digitalWrite( out_pin[i], LOW ) ; 
      } 
    
    }
    
    void loop() { 
    
      // Automation controller
      gw.process();
    
      // Idle flag
      idle = true; 
    
      // gap step calculation 
      gap = 0.0; 
      
      // Smooth transition to target level 
      for (int i=0; i<3; i++) { 
        if ( level[i] != level_tg[i] )  { 
           gap = ( ( level_tg[i] - level[i] ) / 25.0 );
           if ( gap > 0.0 && gap < 1.0 ) gap = 1.0 ;
           if ( gap < 0.0 && gap > -1.0 ) gap = -1.0;        
                 level[i] += gap;              
        } 
      }  
      
      // Small delay to produce the fade effect 
      if ( gap != 0 )   delay(27);  // Aprox. .7s  (=700/25) of ramp up/down 
           
      // Read the Rotary Encoders
      for (int i=0; i<2; i++) { 
    
        // Read current state
        a = digitalRead( rotary[i][0] );
        b = digitalRead( rotary[i][1] ); 
    
        // Any change? 
        if ( a != oldA[i] || b != oldB[i] ) { 
    
          idle = false; 
          idle_ct = 0; 
    
          if ( oldA[i] == LOW && oldB[i] == LOW ) {  
    
            a == HIGH ?  level[i]-=8 : level[i]+=8 ; 
    
            if ( level_tg[i] > 100 )  level_tg[i] = 100; 
            if ( level_tg[i] < 0 )    level_tg[i] = 0; 
    
          }
      
          // Save to use later... 
          oldA[i] = a;
          oldB[i] = b; 
          delay(3);  
        }  
      } 
    
      // Read local Push button
      for (int i=0; i<3; i++) { 
        
        // Check SWs
        newSw = digitalRead( sw[i] ); 
    
        // Any change? 
        if ( newSw != oldSw[i] ) { 
    
          // Not idle anymore... 
          idle = false; 
          idle_ct =  0; 
    
          // Minimum push to swap state
          if ( millis() - lastSw[i] > 500 ) { 
    
            level_tg[i] > 0 ? level_tg[i] = 0 : level_tg[i] = 100; 
    
          } 
    
          // Save to use later... 
          oldSw[i] = newSw; 
          lastSw[i] = millis();
          delay(50); 
    
        } 
    
      }
    
      if (idle) { 
    
        if ( ++idle_ct > 15000 ) {   // (about 1s) 
    
          // Check for not-updated levels on GW-controller
          for (int i=0; i<3; i++) { 
    
            // Update Level in GW-controller
            if ( level_gw[i] != level[i] ) { 
    
              // update GW-controller
              // Send new state and request ack back
              gw.send( msg1[i].set( level[i] ) ); 
              gw.send( msg2[i].set( level[i] > 0 ? true : false ) );  
              level_gw[i] = level[i]; 
    
            } 
    
            // Warning message for no zero-cross detected 
            if ( !AC_found ) { 
              gw.send( var1.set( "No AC?") ) ; 
              AC_found = true ; 
            } 
          }
    
          // Reset idle counter
          idle_ct = 0; 
        } 
    
      } 
    
    } 
    
    
    void incomingMessage(const MyMessage &message)
    {
    
      // ACK
      if (message.isAck()) {
        // Nothing to do, just ACK from Gateway
        //irsend.send(NEC, 0x1EE17887, 32); // Vol up yamaha ysp-900
       // irsend.send(NEC, 0x1EE1F807, 32); // Vol down yamaha ysp-900
      } 
      else { 
    
        // IR Output 
        if ( message.type == V_IR_SEND )
        { 
          
         // TO DO 
          
        } 
        
        // Light/Dimmer messages 
        if ( message.type == V_LIGHT || message.type == V_DIMMER ) 
        {
    
          //  Retrieve the power or dim level from the incoming request message
          int requestedLevel = atoi( message.data );
    
          // Channel
          int i = message.sensor; 
    
          // Adjust incoming level if this is a V_LIGHT variable update [0 == off, 1 == on]
          requestedLevel *= ( message.type == V_LIGHT ? 100 : 1 );
    
          // Clip incoming level to valid range of 0 to 100
          requestedLevel = requestedLevel > 100 ? 100 : requestedLevel;
          requestedLevel = requestedLevel < 0   ? 0   : requestedLevel;
    
          // Set new level
          level_tg[i] = requestedLevel; 
          level_gw[i] = requestedLevel; 
    
        }
      }
    }
    
    
    My Project

  • MyS not working on solar sensor board
    rvendrameR rvendrame

    @ramwal

    f6eeb556-f957-4f82-95d2-c8be09b606d3-image.png

    These boards use a different CE and CS pins in Arduino. Try to add this BEFORE the #include <MySensors.h>:

    #define MY_RF24_CE_PIN 7 // Ceech Arista board 
    #define MY_RF24_CS_PIN 8 // Ceech Arista board
    // Includes -------------------------------------------
    #include <MySensors.h> 
    
    Troubleshooting

  • Safe In-Wall AC to DC Transformers??
    rvendrameR rvendrame

    More from the 'guru'

    "There is no input fuse, that is the reason I recommend one and it has to be a real fuse that blows, not a fuse that will automatic recover. The only time it is supposed to blow is if the converter blows and then you want the mains permanently disconnected. Probably a 0.2A slow fuse will work."

    Hardware

  • AC light dimmer with 2x TRIAC
    rvendrameR rvendrame

    @jacikaas

    Try changing this line (not tested):

     int dimtime = (75*currentLevel);    // For 60Hz =>65    
    
    

    to

     int dimtime = (75* ( 128-currentLevel) );    // For 60Hz =>65    
    
    

    Also remove this line to avoid the flicker:

    
      analogWrite( LED_PIN, (int)(currentLevel / 100. * 255) );
    

    ... and finally add this line right before the "fadeToLevel( requestedLevel );" , in order to convert 0-100 range into 0-128 range:

     requestedLevel = map( requestedLevel, 0 , 100 , 0 , 128 ); 
    

    Good luck!

    My Project

  • Which gateway to choose Serial, Ethernet or MQTT?
    rvendrameR rvendrame

    ... and the serial gateway usually is powered by the host USB computer/controller, while the ethernet gateway requires its own power adapter.

    Just adding my two cents ;-)

    General Discussion

  • Safe In-Wall AC to DC Transformers??
    rvendrameR rvendrame

    @petewill . the MOV goes in parallel with HLK input (and not in series like you did).

    It was suggested to use .2A fuses (instead the .75A). And the Thermal fuse should be glued on HLK top (my guess).

    Hardware

  • AC light dimmer with 2x TRIAC
    rvendrameR rvendrame

    @jacikaas

    You must declare variables as 'volatile' to ensure they will be updated during interrupts. Try changing:

    static int currentLevel = 128;  // Current dim level...
    

    into

    volatile int currentLevel = 128;  // Current dim level...
    

    And to ensure no triac firing at zero level, try something like this at beginning of zero_cross_int() (before the "int dimtime"):

    if ( currentLevel == 0 )  return; 
    

    Good luck again!

    My Project

  • Over the air (OTA) bootloading update tutorial?
    rvendrameR rvendrame

    @tekka, please add OTA Serial monitor into the Santa Klaus list ;-)

    General Discussion

  • Mosfet with Ceech board
    rvendrameR rvendrame

    You need to connect the LED (+resistor) like this:

    • Positive: Vcc Pin
    • Negative: The pin with the orange/brown wire in your picture.
      the mosfet onboard is N-channel, so it will switch the negative (GND) line.
    Hardware

  • 💬 OH 433Mhz Mysensors Bridge
    rvendrameR rvendrame

    @LastSamurai , thanks for the great work. I just built one of these and it is working great.

    If your controller accepts long scene numbers (like my Vera does), we have the option to present it as a scene controller, instead individual buttons. The scene number will be the number of the button pressed. That makes life easy to add new panels around the house.

    Bellow is the sketch I created, based on your example. I also added a short/long-press logic, so for example for a 3-button panel, 6 different scenes can be triggered.

    /*
      This is a gateway node between mysensors and 433Mhz devices. A 433Mhz receiver is connected to pin 3 (int 1).
      This works with any 433Mhz device (as long as rc-switch understands the protocol).
    
      This is based on the MySensors network: https://www.mysensors.org
      This uses the rc-switch library to detect button presses: https://github.com/sui77/rc-switch
    
      Written by Oliver Hilsky 07.03.2017
      Adjusted to Scene controller by Ricardo Vendrame  03.06.2017 
    */
    
    #define LONG_PRESS_OFFSET 10000000UL  // Offset for long-press
    #define LONG_PRESS_COUNT  5           // Number of messages for long-press
    
    #define MY_RADIO_NRF24
    //#define MY_NODE_ID 25
    #define MY_BAUD_RATE 115200
    #define MY_DEBUG    // Enables debug messages in the serial log
    #define VCC_PIN 5
    #define GND_PIN 2
    #define RECEIVER_PIN 3
    
    //#include <EEPROM.h>
    #include <SPI.h>
    #include <MyConfig.h>
    #include <MySensor.h>
    //#include <Vcc.h>
    #include <RCSwitch.h>
    
    MySensor gw; 
    MyMessage msgSceneOn( 0 , V_SCENE_ON ); 
    MyMessage msgSceneOff( 0, V_SCENE_OFF ); 
    
    RCSwitch wirelessSwitch = RCSwitch();
    
    // Short/long press control 
    unsigned long lastreceive, count; 
    
    // add the decimal codes for your switches here
    unsigned long received_val; 
    
    void presentation()
    {
       
    #ifdef MY_NODE_ID
      gw.begin(NULL, MY_NODE_ID, false);
    #else
      gw.begin(NULL,AUTO,false);
    #endif
    
      gw.sendSketchInfo("433 Wireless Gateway", "1.0");
      gw.present( 0 , S_SCENE_CONTROLLER );
    
    }
    
    void setup() {
      
      Serial.begin(MY_BAUD_RATE); 
    
      // Power to Radio... 
      pinMode( VCC_PIN , OUTPUT ); 
      pinMode( GND_PIN , OUTPUT ); 
      digitalWrite( GND_PIN , LOW ); 
      digitalWrite( VCC_PIN , HIGH ); 
    
      // mySensors startup
      presentation(); 
      
      // Enable 433 receiving 
      wirelessSwitch.enableReceive(RECEIVER_PIN-2);  // Receiver on interrupt 
      
      Serial.println("Started listening on pin 3 for incoming 433Mhz messages");
    
      lastreceive = millis(); 
      
    }
    
    void loop() {
    
      gw.process(); 
      
      if ( wirelessSwitch.available() ) {  
    
        if ( ( millis() - lastreceive ) <= 200 ) count ++;
          else count = 1; 
          
        received_val = wirelessSwitch.getReceivedValue();
        wirelessSwitch.resetAvailable();
    
        lastreceive = millis(); 
        //Serial.println( lastreceive ); 
        
      }
    
      if ( count >= LONG_PRESS_COUNT || 
       ( ( millis() - lastreceive ) > 200 && count > 0 ) ) {
        
        // Send scene ID to the gateway
        if ( count >= LONG_PRESS_COUNT ) 
           received_val += LONG_PRESS_OFFSET;
            
        gw.send( msgSceneOn.set( received_val )); 
        
        Serial.print("Scene ");
        Serial.print( received_val ); 
        Serial.println( count < LONG_PRESS_COUNT ? " ON" : " OFF"); 
    
        // software debounce
        gw.wait(750); 
        //delay(750);
        count = 0; 
        wirelessSwitch.resetAvailable();
    
    
      } 
    
    }
    
    

    The edge for short/long press is defined by count of messages received (each message takes about 100ms to be received), so the default "5" in the sketch defines >= ~500ms as long press.

    #define LONG_PRESS_COUNT  5           // Number of messages for long-press
    

    And to distinguish between short and long press, a offset is added to the button number, defined here:

    #define LONG_PRESS_OFFSET 10000000UL  // Offset for long-press
    
    OpenHardware.io contest2017 mysensors 433mhz

  • Will the Arduino mini 3.3v give enough stable power for nRF ?
    rvendrameR rvendrame

    @ahmedadelhosni , for a definitive answer you must check what kind of regulator your board has, as well as how much each component of your circuit will consume.

    The official pro-mini uses an MIC5205 voltage regulator that can deliver maximum of 150mA.

    Most clones uses a cheaper regulator (such as the 100mA LP2981 that I found in my pro-minis).

    The official nRF24l01 radio consumes about 15mA while transmitting/receiving. You have to add on top of it the arduino itself, as well as any other components that you are powering up.

    And to be on safe side I would not use more than 60-70% of nominal regulator's output.

    Hope it helps...

    General Discussion

  • TV on/off monitor using TV USB port
    rvendrameR rvendrame

    Just brainstorming: a Door/Window sensor with 2xAA batteries may last ~2 years, if you remove the led and regulator. You just need to connect the USB - to GND and USB + to D3 via a diode.

    Another option is to replace the 2xAA by a small lithium battery, with one of these cheap TP4056 charger boards, so the USB from TV will keep the battery charged. You just need to connect +VCC (from the USB connector side) to D3 via a diode.

    https://www.aliexpress.com/w/wholesale-board-tp4056.html
    https://www.aliexpress.com/wholesale?catId=0&initiative_id=SB_20200427084513&SearchText=small+lithium

    Hardware

  • 💬 Micro (nano) ampere meter (double)
    rvendrameR rvendrame

    @Nca78 , updating: I figured out how to connect the load, so I'm done with it (including calibration).

    I changed the original code a bit, as I also wanted it a bit more responsive. I didn't lift pin 15 as you did.

    BTW, what bootloader + clock are you using? Perhaps that could influence? I'm using MYSBootloaderV13pre.hex, 8Mhz internal clock...

    Changes:

    • Less average reading per cycle (from 32 to 4 ) --> Didn't notice significant changes in measurements.
    • More accumulated avg reads ( from 16 to 32 ) --> Just to keep a less volatile avg numbers in display.
    • Change the logic of short/long press. short press (~1s) = change mode, long press (~3s) = Offset.

    In case you want to give it a try...

    // uA meter with HX711
    /*
     PROJECT: MySensors - uA meter with HX711
     PROGRAMMER: AWI
     DATE: 20170414/ last update: 
     FILE: AWI_uA_meter.ino
     LICENSE: Public domain
    
     Performance improvements: rvendrame 
     
    Hardware: tbd Nano ATmega328p board w/ NRF24l01
    	
    Special:
    	program with Arduino Nano
    	
    SUMMARY:
    	Measures mV accross a shunt resistor ~ uA - channel A
    	Measures mV on channel B
    	Modes:
    		- default: measure uV in full resolution (Stable reading only for 0.1uV)
    		- other:
    			A: channel A: default, amplification 128 - div 500: 0.1uV stable,  range +/- 20mV, (1ohm +/- 20mA, res 100 nA)
    			B: channel B: amplification 32 - div 125: 100nA stable, range +/- 80mV,  (10 ohm +/- 8 mA, res 10 nA)
    			AB: both channels:  
    		- uA - calibration: depending on the actual shunt:
    			0.47 ohm -> 1 uV ~ 2uA, range -40 mA - 40 mA
    			1 ohm -> 1 uV = 1uA, range -20 mA - 20 mA
    			10 ohm -> 1 uv = 0.1uA
    		- mV - calibration, depend on amplification
    	Button switch:
    		- Short press, reset current channel to offset 0 (keep terminals shorted, no need with uA ;-)
    		- Long press, change channel A (uA) / B(uA)/ A & B (uA)
    		
    	Hx711 24bit weight scale sensor
    		- Noise and temperature sensitive (x bit effective)
    	OLED 128x64 display
    	
    Remarks:
    	Size is large as result of font library for display
    update:
    	
    */
    
    #include <U8g2lib.h>									// U8glib for OLED display
    #include <Wire.h> 										// I2C
    #include <Button.h>										// https://github.com/JChristensen/Button
    #include "HX711.h"										// local ADC lib
    
    const double calibrationFactorA = 599.18f ;				// calibration for channel A: set to 1.0 for known current and divide
    const double calibrationFactorB = 149.76f ;				// calibration for channel B: set to 1.0 for known current and divide
    long offsetChannelA = 0 ;								// channel offsets for A and B (drifts) are calibrated at startup and on command. 
    long offsetChannelB = 0 ;
    
    const uint8_t HX711_dout = A1 ;							// HX711 data out pin
    const uint8_t HX711_sck = A0 ;							// HX711 serial clock
    const uint8_t buttonPin = A2 ;							// connects the button to select function and reset offset
    //const unsigned long longPress = 1500UL ;				//	- long press set reference temperature - in ms												// 	- when alarm, short press resets alarm	
    Button myBtn(buttonPin, true,  true, 40);				// Declare the button( pin, pullup, invert, debounce ms)
    
    enum convertMode_t {channelA, channelB, channelAB} ;	// measurement modes, 32 port B / 128 port A / A & B
    
    HX711 scale;											// instantiate ADC
    
    // U8G instantiate, Change this constructor to match the display!!!
    U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);   // All Boards without Reset of the Display
    
    const int nettReadingsSize = 32 ; 						// the number of readings to determine the average and calculate variance/ accuracy
    double lastReading, lastReadingB ; 
    double nettReadings[nettReadingsSize] ; 				// store the rolling average of readings
    int nettReadingPointer = 0 ; 
    
    convertMode_t convertMode = channelA ;					// default channelA
    
    enum state_t {idleState, waitForRelease} ;      // define possible states
    static state_t state = idleState ;  
    
    
    void setup() {
    	Serial.begin(115200);
    
      Serial.println("AWI uA meter");
    
    	// u8g setup
    	u8g.begin() ;
    	u8g.setFont(u8g2_font_helvR14_tf);					// 'r' = reduced (or 'n' = numeric) font only for size
    	//u8g.setFont(u8g2_font_profont15_tf);					// 'r' = reduced (or 'n' = numeric) font only for size
    
    	// HX711.DOUT	- pin #A1
    	// HX711.PD_SCK	- pin #A0
    	// if parameter "gain" is ommited; the default value 128 is used by the library
    	//   64 & 128 is port A ; 32 is port B
    	scale.begin(HX711_dout, HX711_sck, 128); 			// set port based on state of selection
    
    	LCD_banner("Initializing") ;
    	Serial.print("read average: \t\t");
    	Serial.println(scale.read_average(20));  			// print the average of 20 raw readings from the ADC
    	
    	getOffset();										// get the offsets (drift values)
    	scale.set_offset(offsetChannelA) ;					// set it for measured channel
    	scale.set_scale(calibrationFactorA);				// this value is obtained by calibrating with known value; see the README for details
    	
    	Serial.print("read: \t\t");
    	Serial.println(scale.read());						// print a raw reading from the ADC
    	Serial.print("read average: \t\t");
    	Serial.println(scale.read_average(10));				// print the average of 20 readings from the ADC
    	Serial.print("get value: \t\t");
    	Serial.println(scale.get_value(5));					// print the average of 5 readings from the ADC minus the tare weight, set with tare()
    	Serial.print("get units: \t\t");
    	Serial.println(scale.get_units(5), 3);				// print the average of 5 readings from the ADC minus tare weight, divided by scale
    	Serial.println("Readings:");
    }
    
    void loop() {
    
    	//Serial.print("one reading:\t");
    	//Serial.print(scale.get_units(), 1);
    	//Serial.print("\t| average:\t");
    	//Serial.println(scale.get_units(30), 3);
    
      checkButton(); 
      
    	// get ADC readings dependent on setting: read A, B or A & B
    	// only A reads has average buffer when A&B mode is selected
    	if (convertMode == channelA){
    		scale.set_gain(128) ;
    		scale.set_offset(offsetChannelA) ;
    		scale.set_scale(calibrationFactorA );			// set division to A value and set mode to A
    		lastReading = scale.get_units(4) ; 			// get value (average 4 readings)corrected with scaling
    		nettReadings[nettReadingPointer] = lastReading ;	// store readings in averagebuffer
    		nettReadingPointer = (++nettReadingPointer) % nettReadingsSize ; // increment and wrap
        checkButton(); 
        LCD_local_display();
    	} else if (convertMode == channelB){
    		scale.set_gain(32) ;
    		scale.set_offset(offsetChannelB) ;
    		scale.set_scale(calibrationFactorB);			// set division to B value and set mode to B
    		lastReading = scale.get_units(4) ; 		  	// get value (average 4 readings)corrected with scaling
    		nettReadings[nettReadingPointer] = lastReading ;	// store readings in averagebuffer
    		nettReadingPointer = (++nettReadingPointer) % nettReadingsSize ; // increment and wrap
        checkButton(); 
        LCD_local_display();
    	} else if (convertMode == channelAB){				// if both channels average 128 readings iso 32 (no buffer)
    		scale.set_gain(128) ;
    		scale.set_offset(offsetChannelA) ;
    		scale.set_scale(calibrationFactorA);			// set division to A value and set mode to A
    		lastReading = scale.get_units(2) ; 		  	// get value (average 4 readings)corrected with scaling
    		checkButton(); 
    		scale.set_gain(32) ;
    		scale.set_offset(offsetChannelB) ;
    		scale.set_scale(calibrationFactorB);			// set division to A value and set mode to A
    		lastReadingB = scale.get_units(2) ; 			// get value (average 4 readings) corrected with scaling
        checkButton(); 
    		LCD_local_displayAB();
    	}
    	//scale.power_down();			       				// put the ADC in sleep mode
    	//delay(500);
    	//scale.power_up();
    	//delay(100);
    }
    
    void checkButton() { 
      
      myBtn.read();                   // read button state
      
      switch (state){
         case idleState:                  // Idle
          if (myBtn.wasPressed()) {       // Pressed 
            // change channel and wait release
            state = waitForRelease ;
          }
          break ;
        case waitForRelease:  
          if (myBtn.pressedFor(3000UL)) {  // Long Press 
             LCD_banner("Offset");
             getOffset();              
             state = idleState; 
          } else if (myBtn.wasReleased()) { // Short Press  
            state = idleState;
            switchMode() ;
          }
          break ;
        
      }
      
    }
    void LCD_banner(const char *s){
    /* prints all avaiable variables on LCD display with units
    	input: all "last" variables
    */
    	u8g.firstPage();
    	do {
    		int strWidth = u8g.getStrWidth(s) ;				// get the length of the string to determine print position
    		u8g.drawStr((128- strWidth)/2, 40, s ) ;			// print right aligned 
    	} while (u8g.nextPage()) ;
    }
    
    
    void LCD_local_display(void){
    /* prints all avaiable variables on LCD display with units
    	input: all "last" variables
    */
    	char buf[21];  										// buffer for max 20 char display
    	char lastNettBuf[14];
    	dtostrf(lastReading, 10, 2, lastNettBuf);			// Convert real to char
    	char averageNettBuf[14];
    	dtostrf(nettReadingsAverage(), 10, 2, averageNettBuf);	// Convert real to char
    	char spreadNettBuf[14];
    	dtostrf(nettReadingsSpread(), 10, 2, spreadNettBuf);	// Convert real to char
    	Serial.print("Average: \t") ; Serial.print(nettReadingsAverage());
    	Serial.print("\tSpread: \t") ; Serial.println(nettReadingsSpread());
    
    	u8g.firstPage();
    	do {
        checkButton(); 
    		snprintf(buf, sizeof buf, "Current %s", (convertMode==channelB)?"B":"A"); // Header
    		int strWidth = u8g.getStrWidth(buf) ;			//   length of the string to determine print position
    		u8g.drawStr((128- strWidth)/2, 14, buf ) ;		//   print middle aligned 
    		u8g.drawStr(0,31,"I") ;							// Current
    		snprintf(buf, sizeof buf, "%10s\xB5\A", lastNettBuf);
    		strWidth = u8g.getStrWidth(buf) ;				//   length of the string to determine print position
    		u8g.drawStr((128- strWidth), 31, buf ) ;		//   print right aligned 
    		u8g.drawStr(0,47,"avg") ;						// Average current
    		snprintf(buf, sizeof buf, "%10s\xB5\A", averageNettBuf);
    		strWidth = u8g.getStrWidth(buf) ;				// get the length of the string to determine print position
    		u8g.drawStr((128- strWidth), 47, buf ) ;		// print right aligned 
    		u8g.drawStr(0,63,"d\xB1") ;						// delta +/-
    		snprintf(buf, sizeof buf, "%10s\xB5\A", spreadNettBuf);
    		strWidth = u8g.getStrWidth(buf) ;				// get the length of the string to determine print position
    		u8g.drawStr((128- strWidth), 63, buf ) ;		// print right aligned 
    	} while (u8g.nextPage()) ;
    }
    void LCD_local_displayAB(void){
    /* prints A & B channel on LCD display with units
    	input: all "last" variables
    */
    	char buf[21];  										// buffer for max 20 char display
    	char lastNettBuf[14];
    	dtostrf(lastReading, 10, 2, lastNettBuf);			// Convert real to char
    	char lastNettBufB[14];
    	dtostrf(lastReadingB, 10, 2, lastNettBufB);			// Convert real to char
    	char lastNettBufAB[14];
    	dtostrf(lastReading +lastReadingB, 10, 2, lastNettBufAB);	// Convert real to char for added values
    	u8g.firstPage();
    	do {
        checkButton(); 
    		snprintf(buf, sizeof buf, "Current A+B"); 		// Header
    		int strWidth = u8g.getStrWidth(buf) ;			//   length of the string to determine print position
    		u8g.drawStr((128- strWidth)/2, 14, buf ) ;		//   print middle aligned 
    		u8g.drawStr(0,31,"IA");							// Current A
    		snprintf(buf, sizeof buf, "%10s\xB5\A", lastNettBuf);
    		strWidth = u8g.getStrWidth(buf) ;				//   length of the string to determine print position
    		u8g.drawStr((128- strWidth), 31, buf ) ;		//   print right aligned 
    		u8g.drawStr(0,47,"IB");							// Current B
    		snprintf(buf, sizeof buf, "%10s\xB5\A", lastNettBufB);
    		strWidth = u8g.getStrWidth(buf) ;				//   length of the string to determine print position
    		u8g.drawStr((128- strWidth), 47, buf ) ;		//   print right aligned 
    		u8g.drawStr(0,63,"A+B");						// Current A + B
    		snprintf(buf, sizeof buf, "%10s\xB5\A", lastNettBufAB);
    		strWidth = u8g.getStrWidth(buf) ;				//   length of the string to determine print position
    		u8g.drawStr((128- strWidth), 63, buf ) ;		//   print right aligned 
    	} while (u8g.nextPage()) ;
    }
    
    // calculate average of nett readings
    double nettReadingsAverage() {
    	double sum = 0;
    	for (byte i = 0; i < nettReadingsSize; i++) {
    		sum += nettReadings[ i ];
    	}
    	return sum / nettReadingsSize;
    }
    
    // calculate spread of nett readings (+/-)
    double nettReadingsSpread() {
    	double minReading = nettReadings[0];
    	double maxReading = minReading ;
    	for (byte i = 1; i < nettReadingsSize; i++) {
        checkButton(); 
    		if (minReading > nettReadings[ i ]){
    			minReading = nettReadings[i] ;
    		}
    		if (maxReading < nettReadings[ i ]){
    			maxReading = nettReadings[i] ; 
    		}
    	}
    	return (maxReading - minReading)/2 ;
    }
    
    // switch the mode
    void switchMode(){
    	if (convertMode == channelA){
    		convertMode = channelB ;
    	} else if (convertMode == channelB){
    		convertMode = channelAB ;
    	} else {
    		convertMode = channelA ;
    	}
    }
    
    // assuming both channels are shorted, calculate the offset values for channel A and B
    double getOffset(){
    	scale.set_gain(128) ;							// get channel A
    	offsetChannelA = scale.read_average(32) ;		// average 512 readings for offset
    	Serial.print("Offset A: \t") ; 
    	Serial.println(offsetChannelA);
    	scale.set_gain(32) ;							// get channel B
    	offsetChannelB = scale.read_average(32) ;		// average 512 readings for offset
    	Serial.print("Offset B: \t") ; 
    	Serial.println(offsetChannelB);
    }```
    OpenHardware.io measurement
  • Login

  • Don't have an account? Register

  • Login or register to search.
  • First post
    Last post
0
  • MySensors
  • OpenHardware.io
  • Categories
  • Recent
  • Tags
  • Popular