enesbcs / rpieasy

Easy MultiSensor device based on Raspberry PI
GNU General Public License v3.0
162 stars 33 forks source link

Support Pro Mini Extender #1

Closed happytm closed 5 years ago

happytm commented 5 years ago

Thank you for starting great concept. I am user of great Espeasy Software on esp8266 for long time. RPIeasy could be very useful if there is I2C bridge with arduino pro mini thereby getting rid of some limitations of RPI. Whatever RPI can not do should be done by Pro Mini.

Thanks for great start.

enesbcs commented 5 years ago

Where can i find instructions about creating and programming the Pro Mini? Without a working PME, i can not start even...

happytm commented 5 years ago

Here is the sketch I use with ESPeasy

https://github.com/letscontrolit/ESPEasySlaves/tree/master/MiniProExtender

I has not been updated since long time but it works fine with esp8266 using ESPeasy software with above plugin included.

I use a custom sketch to do some extra task on Pro Mini as below.

/****\

include

include // Avaiable from http://www.arduino.cc/playground/Code/Timer1

int triac; int dim; volatile int i=0; // Variable to use as a counter volatile boolean zero_cross=0; // Boolean to store a "switch" to tell us if we have crossed zero int triac1; // Output to Opto Triac1 int triac2; // Output to Opto Triac2 int triac3; // Output to Opto Triac3 int triac4; // Output to Opto Triac4 int triac5; // Output to Opto Triac5

//int POT_pin = A0; // Pot for testing the dimming int LED = 13; // LED for testing int dim1; // Dimming level (0-128) 0 = on, 128 = 0ff int dim2; // Dimming level (0-128) 0 = on, 128 = 0ff int dim3; // Dimming level (0-128) 0 = on, 128 = 0ff int dim4; // Dimming level (0-128) 0 = on, 128 = 0ff int dim5; // Dimming level (0-128) 0 = on, 128 = 0ff

int freqStep = 65; // or 78 based on power supply : This is the delay-per-brightness step in microseconds. // It is calculated based on the frequency of your voltage supply (50Hz or 60Hz) // and the number of brightness steps you want. // // The only tricky part is that the chopper circuit chops the AC wave twice per // cycle, once on the positive half and once at the negative half. This meeans // the chopping happens at 120Hz for a 60Hz supply or 100Hz for a 50Hz supply.

// To calculate freqStep you divide the length of one full half-wave of the power // cycle (in microseconds) by the number of brightness steps. // // (1000000 uS / 120 Hz) / 128 brightness steps = 65 uS / brightness step or (1000000 uS / 100 Hz) / 128 brightness steps = 78 uS / brightness step // // 1000000 us / 120 Hz = 8333 uS, length of one half-wave or 100 Hz =

define I2C_MSG_IN_SIZE 4

define I2C_MSG_OUT_SIZE 4

define CMD_DIGITAL_WRITE 1

define CMD_DIGITAL_READ 2

define CMD_ANALOG_WRITE 3

define CMD_ANALOG_READ 4

volatile uint8_t sendBuffer[I2C_MSG_OUT_SIZE]; int ESPrms; // value to be sent over I2C to ESP

void setup() { Wire.begin(0x7f); Wire.onReceive(receiveEvent); Wire.onRequest(requestEvent); //Serial.begin(9600);

pinMode(triac1, OUTPUT); // Set the Triac1 pin as output pinMode(triac2, OUTPUT); // Set the Triac1 pin as output pinMode(triac3, OUTPUT); // Set the Triac1 pin as output pinMode(triac4, OUTPUT); // Set the Triac1 pin as output pinMode(triac5, OUTPUT); // Set the Triac1 pin as output

pinMode(LED, OUTPUT); // Set the LED pin as output

attachInterrupt(0, zero_cross_detect, RISING); // Attach an Interupt to Pin 2 (interupt 0) for Zero Cross Detection Timer1.initialize(freqStep); // Initialize TimerOne library for the freq we need Timer1.attachInterrupt(dim_check, freqStep);
// Use the TimerOne Library to attach an interrupt // to the function we use to check to see if it is // the right time to fire the triac. This function // will now run every freqStep in microseconds. }

/* ** This part in the loop found here: [url] https://forum.arduino.cc/index.php?topic=179541.0 [/url] tnx to dc42 constant 75.7576 depends on the sensitivity of the ACS712 module Sensitivity Min<Typ<Max mV/A for 30A: 64< 66< 68 mV/A > constant = 5/.066 = 75.76 for 10A: 96<100<104 mV/A > constant = 5/.1 = 50.00 for 5A: 180<185<190 mV/A > constant = 5/.185 = 27.03 */

const int currentPin = A0; // ADC pin used for ACS712

const unsigned long sampleTime = 100000UL; // sample over 100ms, it is an exact number of cycles for both 50Hz and 60Hz mains const unsigned long numSamples = 250L; // number of samples 400 microsecond each const unsigned long sampleInterval = sampleTime/numSamples; // sampling interval, must be longer than then ADC conversion time int adc_zero = 514; // relative zero of the ACS712 for me 514 was best. 511 int valueRead; // value read on ADC

void zero_cross_detect() {
zero_cross = true; // set the boolean to true to tell our dimming function that a zero cross has occured i=0; // since the control pin stays high, the TRIAC won't 'unlatch' // when zero-crossing, so I need to put the pins to LOW digitalWrite(triac1, LOW); digitalWrite(triac2, LOW); digitalWrite(triac3, LOW); digitalWrite(triac4, LOW); digitalWrite(triac5, LOW);

// writing pins is like 10 times faster if // we write the register directly // instead of using 'digitalWrite'

}

// Turn on the TRIAC at the appropriate time void dim_check() {
if(zero_cross == true) {

if(i>=dim1)  {                     
  digitalWrite(triac1, HIGH);  // turn on triac1
  i=0;  // reset time step counter                         
  zero_cross = false; //reset zero cross detection
}   

 if(i>=dim2) {                     
  digitalWrite(triac2, HIGH);  // turn on triac2 
  i=0;  // reset time step counter                         
  zero_cross = false; //reset zero cross detection
}   

  if(i>=dim3) {                     
  digitalWrite(triac3, HIGH);  // turn on triac3
  i=0;  // reset time step counter                         
  zero_cross = false; //reset zero cross detection
}   

 if(i>=dim4) {                     
  digitalWrite(triac4, HIGH);  // turn on triac4
  i=0;  // reset time step counter                         
  zero_cross = false; //reset zero cross detection
}   

if (i>=dim5) {                     
  digitalWrite(triac5, HIGH);  // turn on triac5
  i=0;  // reset time step counter                         
  zero_cross = false; //reset zero cross detection
}    
else {

  i++; // increment time step counter 

}

}                                

}

void loop()

{

     if (triac == 4) {
        dim1 = dim / 8;

      }
     if (triac == 5) {
        dim2 = dim / 8; 
      }

      if (triac == 6) {
        dim3 = dim / 8; 
      }
     if (triac == 7) {
        dim4 = dim / 8; 
      }

     if (triac == 8) {
        dim5 = dim / 8; 
      }

//dim = dim / 8; // set dimmer value to global integer dim //dim = analogRead(POT_pin) / 8; // read dimmer value from potentiometer //analogWrite(LED, dim); // write dimmer value to the LED, for debugging

/* Serial.print(dim); Serial.print("dim = "); Serial.print("triac = "); Serial.print(triac); Serial.print("dim1 = "); Serial.print(dim1);

Serial.print("triac1 = ");

Serial.print(triac1); Serial.print("dim2 = "); Serial.print(dim2) ; Serial.print("triac2 = "); Serial.print(triac2); Serial.print("dim3 = "); Serial.print(dim3); Serial.print("triac3 = "); Serial.print(triac3); Serial.print("dim4 = "); Serial.print(dim4); Serial.print("triac4 = "); Serial.print(triac4); Serial.print("ESPrms = "); Serial.print(ESPrms);

delay(1000); Serial.print('\n');

unsigned long currentAcc = 0; unsigned int count = 0; unsigned long prevMicros = micros() - sampleInterval ;

while (count < numSamples)

{ if (micros() - prevMicros >= sampleInterval) { long adc_raw = analogRead(currentPin) - adc_zero; currentAcc += (unsigned long)(adc_raw * adc_raw); ++count; prevMicros += sampleInterval; } }

float rms = sqrt((float)currentAcc/(float)numSamples) (27.03 / 1024.0); // see note above for this 27.03 value ESPrms = 1000rms; // conversion of float Ampere into integer milliAmpere needed for I2C communication

/ } / ** */

void receiveEvent(int count) { if (count == I2C_MSG_IN_SIZE) { byte cmd = Wire.read(); byte port = Wire.read(); int value = Wire.read(); value += Wire.read()*256; switch(cmd) { case CMD_DIGITAL_WRITE: pinMode(port,OUTPUT); digitalWrite(port,value); break; case CMD_DIGITAL_READ: pinMode(port,INPUT_PULLUP); clearSendBuffer(); sendBuffer[0] = digitalRead(port); break; case CMD_ANALOG_WRITE: //analogWrite(port,value); triac=(port); if (port == 4) triac1 = (port); if (port == 5) triac2 = (port);
if (port == 6) triac3 = (port); if (port == 7) triac4 = (port); if (port == 8) triac5 = (port); dim=(value); //dim=dim / 8; //if (port == 4) dim1 = (value); // if (port == 5) dim2 = (value);
// if (port == 6) dim3 = (value); // if (port == 7) dim4 = (value); // if (port == 8) dim5 = (value); break; case CMD_ANALOG_READ: clearSendBuffer(); if (port <= 4) valueRead = analogRead(port); // port <=4 to read analog value A0,A1,A2,A3 - A4 & A5 are I2C if (port == 100) valueRead = ESPrms; // port is any number >4 up to 255 //if (port == 2) selector = analogRead(A2); //if (port == 3) level = analogRead(A3); sendBuffer[0] = valueRead & 0xff; sendBuffer[1] = valueRead >> 8; break; } } }

void clearSendBuffer() { for(byte x=0; x < sizeof(sendBuffer); x++) sendBuffer[x]=0; }

void requestEvent() { Wire.write((const uint8_t*)sendBuffer,sizeof(sendBuffer)); }

happytm commented 5 years ago

As you can see I am using Pro Mini for Mains current measurement and AC dimmer control which are impossible using only RPI or ESP8266. That is the importance of including Pro Mini. Only requirement is communication going between RPI and Pro Mini via I2C bus.

Thanks

enesbcs commented 5 years ago

I found this on ebay, according to its description it is an AT Mega328 3.3V compatible with Pro Mini, i can order this if it will be sufficient to test: https://www.ebay.com/itm/Mini-ATMEAG328-3-3V-8Mhz-Replace-ATmega128-for-Arduino-Pro-Mini-Compatible/173360751835 Otherwise I2C is not a big deal, but the ESPEasy plugin that handles the Pro Mini looks like a little complicated to me, it will not be easy to port it. Sending commands to the Pro Mini may be implemented, but for the reverse way (from ProMini to RPIEasy) currently i have no clue how to store/display data...

happytm commented 5 years ago

I followed user Costo's post on ESPeasy forum to understand how to read values from Pro Mini into ESPeasy. Basically he set up devices in ESPeasy on devices tab to read value from Pro Mini.Link is below

https://www.letscontrolit.com/forum/viewtopic.php?f=4&t=627&p=6385&hilit=pme#p6385

enesbcs commented 5 years ago

So basically we can use Dummy devices to hold values and create one (or more) ProMini devices, in which to be given the destination task and value number(s) (max 10 row per dev?) with type (analog/digital)? I've done something similar (value rerouting) with battery value watching, which can be setted in Advanced settings. (i am using an ADS1015 for this goal now)

The "compatible" device i mentioned in the previous post is right?

happytm commented 5 years ago

Yes device you chose should work fine. For that matter any arduino device should work fine with shift register for 5V device or directly without shift register for 3.3V device. Your device is 3.3V so it should be fine by connecting directly 4 wires (3.3V, GND, SCL & SDA).To store value to dummy device you can use rules if it is implemented in your code.

happytm commented 5 years ago

You will also need FTDI adapter to upload the sketch to Pro Mini like below

https://www.ebay.com/itm/FT232RL-FTDI-Serials-Adapter-Module-Mini-Port-f-USB-to-TTL-3-3V-5-5V-BBC/142727421107?hash=item213b379cb3:g:EKMAAOSwinVZtzmB:rk:1:pf:0

Arduino Uno has this usb to serial chip built in but Pro mini does not.

I live in USA and if you need Pro Mini , FTDI adapter and some extra sensors I have I can send them to you. Please let me know.

Thanks

enesbcs commented 5 years ago

Thanks, but I already have a CP2102 (for flashing Sonoff devices) i think it will work for uploading, and ordered a ProMini for testing. Itt will arrive in time.. If you want to send some other sensors which is currenty not supported, itt will be nice.. but i am living in Hungary, Europe. :)

enesbcs commented 5 years ago

I was reading and tried to interpret the PME code. But i found a possible issue: It's default address 0x7f is outside of the valid I2C range. (0x08-0x77) https://www.totalphase.com/support/articles/200349176-7-bit-8-bit-and-10-bit-I2C-Slave-Addressing I doubt that RPI smbus module will handle it.

happytm commented 5 years ago

Is it not possible to define different address within the range (0x008-0x77) in PME plugin and match same newly defined address in pro mini extender code on arduino ? While searching web I found following links

https://www.raspberrypi.org/forums/viewtopic.php?t=155389 https://www.raspberrypi.org/forums/viewtopic.php?t=170958 https://oscarliang.com/raspberry-pi-arduino-connected-i2c/ (using 0x04 address) https://www.bluetin.io/interfacing/i2c-connect-raspberry-pi-arduino/ (using 0x08 address)

enesbcs commented 5 years ago

Yes, it is absolutely possible, but every PME needs to be reflashed with the recompiled sketch. :) I prefer 0x3F for the new address as this is the valid counterpart for 0x7f in 7bit.

happytm commented 5 years ago

Thank you for start on PME plugin. Can't wait to test once it is implemented.

enesbcs commented 5 years ago

Experimental version added at https://github.com/enesbcs/rpieasy/commit/b0ca9497a4b213ce7ee0de1cea7d033fd4d35039

happytm commented 5 years ago

I started testing this plugin and following is my feedback -

When joystick down -

22:27:46 Event: PMEanalog6#Value=0.0 22:27:46 Event: PMEanalog7#Value=0.0 22:27:47 Event: PMEdigital9#Value=0.0

When joystick in middle -

22:27:50 Event: PMEdigital9#Value=255.0 22:27:50 Event: PMEanalog6#Value=511.0 22:27:50 Event: PMEanalog7#Value=511.0

When joystick up -

22:27:55 Event: PMEanalog6#Value=1023.0 22:27:55 Event: PMEanalog7#Value=1023.0 22:27:56 Event: PMEdigital9#Value=255.0

22:28:00 Event: Clock#Time=Thu,22:28

22:31:52 Event: PMEdigital9#Value=149.0 22:31:52 Event: PMEanalog6#Value=405.0 22:31:52 Event: PMEanalog7#Value=405.0 22:31:53 Event: PMEdigital9#Value=162.0 22:31:53 Event: PMEanalog6#Value=418.0 22:31:53 Event: PMEanalog7#Value=418.0 22:31:54 Event: PMEdigital9#Value=160.0 22:31:54 Event: PMEanalog7#Value=416.0 22:31:54 Event: PMEanalog6#Value=416.0 22:31:55 Event: PMEdigital9#Value=158.0 22:31:55 Event: PMEanalog6#Value=414.0 22:31:55 Event: PMEanalog7#Value=414.0 22:31:56 Event: PMEdigital9#Value=158.0 22:31:56 Event: PMEanalog7#Value=414.0 22:31:56 Event: PMEanalog6#Value=414.0 22:31:57 Event: PMEdigital9#Value=148.0 22:31:57 Event: PMEanalog6#Value=404.0 22:31:57 Event: PMEanalog7#Value=404.0 22:31:58 Event: PMEdigital9#Value=110.0 22:31:58 Event: PMEanalog6#Value=366.0 22:31:58 Event: PMEanalog7#Value=366.0 22:31:59 Event: PMEdigital9#Value=138.0 22:31:59 Event: PMEanalog6#Value=138.0 22:31:59 Event: PMEanalog7#Value=138.0 22:32:00 Event: PMEdigital9#Value=99.0 22:32:00 Event: PMEanalog6#Value=355.0 22:32:00 Event: PMEanalog7#Value=355.0 22:32:00 Event: Clock#Time=Thu,22:32 22:32:02 Event: PMEdigital9#Value=99.0 22:32:02 Event: PMEanalog6#Value=355.0 22:32:02 Event: PMEanalog7#Value=355.0 22:32:03 Event: PMEdigital9#Value=101.0 22:32:03 Event: PMEanalog6#Value=357.0 22:32:03 Event: PMEanalog7#Value=357.0 22:32:04 Event: PMEdigital9#Value=101.0 22:32:04 Event: PMEanalog6#Value=357.0 22:32:04 Event: PMEanalog7#Value=357.0 22:32:05 Event: PMEdigital9#Value=106.0 22:32:05 Event: PMEanalog6#Value=362.0 22:32:05 Event: PMEanalog7#Value=362.0 22:32:06 Event: PMEdigital9#Value=111.0 22:32:06 Event: PMEanalog6#Value=367.0 22:32:06 Event: PMEanalog7#Value=367.0 22:32:07 Event: PMEdigital9#Value=120.0 22:32:07 Event: PMEanalog6#Value=376.0 22:32:07 Event: PMEanalog7#Value=376.0 22:32:08 Event: PMEdigital9#Value=120.0 22:32:08 Event: PMEanalog6#Value=376.0 22:32:08 Event: PMEanalog7#Value=376.0 22:32:09 Event: PMEdigital9#Value=120.0 22:32:09 Event: PMEanalog6#Value=376.0 22:32:09 Event: PMEanalog7#Value=376.0 22:32:10 Event: PMEdigital9#Value=127.0 22:32:10 Event: PMEanalog6#Value=383.0 22:32:10 Event: PMEanalog7#Value=383.0 22:32:11 Event: PMEdigital9#Value=127.0 22:32:11 Event: PMEanalog6#Value=383.0 22:32:11 Event: PMEanalog7#Value=383.0 22:32:12 Event: PMEdigital9#Value=127.0 22:32:12 Event: PMEanalog6#Value=383.0 22:32:12 Event: PMEanalog7#Value=383.0 22:32:13 Event: PMEdigital9#Value=126.0 22:32:13 Event: PMEanalog6#Value=382.0 22:32:13 Event: PMEanalog7#Value=382.0 22:32:14 Event: PMEdigital9#Value=127.0 22:32:14 Event: PMEanalog6#Value=383.0 22:32:14 Event: PMEanalog7#Value=383.0 22:32:15 Event: PMEdigital9#Value=126.0 22:32:15 Event: PMEanalog6#Value=382.0 22:32:15 Event: PMEanalog7#Value=382.0 22:32:16 Event: PMEdigital9#Value=126.0 22:32:16 Event: PMEanalog6#Value=382.0 22:32:16 Event: PMEanalog7#Value=382.0 22:32:17 Event: PMEdigital9#Value=231.0 22:32:17 Event: PMEanalog6#Value=231.0 22:32:17 Event: PMEanalog7#Value=231.0

If I move joystick sideways on both ends it seems there is no effect and values in log remain same as if joystick is middle position.

For output side if at least 1 device is enabled (for reading pin value from pro mini ) then both extpwm & extgpio works fine and turn pin high or low when I issue extpwm or extgpio command from advanced tab of rpieasy. But in original espeasy there was no need to set up pme plugin on device page in order to enable extgpio and extpwm command. Output side worked automatically without setting up anything on device page for pme plugin.

It is very good start and I appreciate your efforts.

Thanks

happytm commented 5 years ago

On further testing I realised that I was using 1 second interval on device page for all 3 pins so i changed interval to 2 seconds on 2 analog pins ( analog 6 & analog 7 ) and then it works reliably as it should. For digital pin 9 on pro mini no matter what interval I set on device page it shows value of 255 but it should be 1 or 0. sorry for confusion. Is it possible to change this behavior so that when switch is pressed or released on pin 9 it send data instantly to rpi instead of waiting for 60 seconds ?

enesbcs commented 5 years ago

Thanks for testing. In RPIEasy, it will sure commands will remain at their specific plugins, so you have activate "Switch" plugin to use gpio and MCP plugin to use extgpio.. for the same reason you have to activate USBRelay plugin to use usbrelay command, and PyGame Sound Player for playaudio command, etc, etc.. The reason is simple: RPIEasy can be run on machines, that do not have GPIO support, for example i running it at my PC also with Ubuntu. And i think every plugin has it's specific task, for good reason.

I am not familiar with the Arduino side of the ProMini, pin number 9 is just forwarded as it written in the text box to the ProMini. As i see the promini code is not capable of sending any data without receiving a read command, so i do not know, how to achieve instantous feedbacks from gpio changes. Is that function works on ESPEasy plugin? Maybe i can send several read commands per second, for ex 10 times per second. Or a new function has to be coded to the promini, that registers attachInterrupt() handler with the selected port and sends data accordingly.

happytm commented 5 years ago

Thank you for your clarification on switch plugin. I will have to test more to see if there can be something done on pro mini code.

enesbcs commented 5 years ago

@happytm: Could you try the new plugin? https://github.com/enesbcs/rpieasy/commit/7373154e620b68eca673402db3e90e57183bf396 Please delete PME devices, than recreate, and set Interval to 0. In this mode plugin asks the selected pin status ten times per second, and if state is changed, than event is fired. Warning: In this mode, i measured approximately +4% CPU usage/pin! So only use, if it is crucial. I tested 1 second interval with two pin and i do not found any problem, and cpu usage is minimal in this mode. (yet) With analog values it will be very noisy, for digital inputs, it could be handy.

happytm commented 5 years ago

I tried new plugin -

enesbcs commented 5 years ago

I guess that analog readings takes more time at the ProMini side, and i have to set delay back to 0.01s. (if data not arrived or arrived a lot of zeros, than it is not a changing and with Interval 0 only the changes logged) On the other hand, digital reads are almost instantous so i left it at 0.001s. I doubt that analog reading is possible with this device at a faster speed. Is it faster with ESP8266? Updated on github.

happytm commented 5 years ago

With esp8266 there are problems with RAM & load so it seems faster some times and it is very slow on other times.Compared to that I feel Rpieasy is way faster.There is too much going on there in the code frequently so reliability is not the best. That is the reason I want to move my projects on Rpieasy platform.Plus I can use RPI for lot of other things like audio, video etc. Analog on promini is something I do not use except to read current sensor ACS712 but I do not need very fast data update for that.I was using joystick to just test the plugin.

enesbcs commented 5 years ago

I see. Could you test the latest update? I hope it fixes analog result mixing.

RPIEasy is still a beta version, and you have to consider the SD card wear leveling problem, when migrating projects to it. But it is really useful if you want some other things like online radio, usb dvb tv, etc. For analog measuring i am using 4 channel ADS1015.

happytm commented 5 years ago

It did not change duplicate values problem. I tried sending data from pro mini in different ways but no success.I created separate integers in pro mini for each pin and sent 7 integers to rpieasy but somehow with every 2 second interval (which I set in plugin ) value becomes same even though when I print those integers values in serial monitor on pro mini it shows separate values for each analog pin correctly.

enesbcs commented 5 years ago

Seems really odd, tried to use a semaphore to not mix results. RPIEasy uses threading by design to run plugin_read commands to speed things up, so it can happen that an analogread command interrupts an other one's response. (ESPEasy is single threaded so it is not a problem there) https://github.com/enesbcs/rpieasy/commit/dce3e3c70f0edec163c1f3f2901072ad0d06e3f7 If latest commit not helps, than i have to extend the arduino sketch to include pin number in the response packet, this is the only way we can sure that results do not mix anymore.

happytm commented 5 years ago

Whatever you did with this commit it seems duplicate values from pro mini no longer happening. I used joystick to test (using 2 seconds interval in plugin for both pins) and it brings separate values from both pins based on real activity of joystick. Thank you.

enesbcs commented 5 years ago

you are welcome. in theory 1 seconds interval might work also. if this operation is adequate, can this issue be closed?

happytm commented 5 years ago

Spoke too soon. it seems like it behaves like before after few minutes.But if I reset pro mini it works properly for few minutes again and same pattern again. May be I2C looses connection and mess up everything.Regarding 1 second interval I tried 1 second interval on both pins but only 1 pin result shows up in log and that too is frozen so it does not respond to joystick anymore and second pin result disappears. strange behavior on 1 second interval. the log is below with comments:

01:07:38 Event: analog6#Value=511.0 // problem starts 01:07:39 Event: analog7#Value=511.0 01:07:40 Event: analog6#Value=511.0 01:07:41 Event: analog7#Value=511.0 01:07:42 Event: analog6#Value=511.0 01:07:43 Event: analog7#Value=511.0 01:07:44 Event: analog6#Value=511.0 01:07:45 Event: analog7#Value=511.0 01:07:46 Event: analog6#Value=511.0 01:07:47 Event: analog7#Value=511.0 01:07:48 Event: analog6#Value=511.0 01:07:49 Event: analog7#Value=511.0 01:07:50 Event: analog6#Value=511.0 01:07:51 Event: analog7#Value=511.0 01:07:52 Event: analog6#Value=511.0 01:07:53 [Errno 121] Remote I/O error // pro mini reset by me manually 01:07:54 [Errno 121] Remote I/O error 01:07:55 [Errno 121] Remote I/O error 01:07:56 Event: analog6#Value=507.0 // from here works fine until automatic reset 01:07:57 Event: analog7#Value=511.0 01:07:58 Event: analog6#Value=507.0 01:07:59 Event: analog7#Value=511.0 01:08:00 Event: analog6#Value=507.0 01:08:00 Event: Clock#Time=Wed,01:08 01:08:01 Event: analog7#Value=511.0 01:08:02 Event: analog6#Value=507.0 01:08:03 Event: analog7#Value=511.0 01:08:04 Event: analog6#Value=507.0 01:08:05 Event: analog7#Value=511.0 01:08:06 Event: analog6#Value=507.0 01:08:07 Event: analog7#Value=511.0 01:08:09 Event: analog6#Value=507.0 01:08:10 Event: analog7#Value=511.0 01:08:11 Event: analog6#Value=507.0 01:08:12 Event: analog7#Value=511.0 01:08:13 Event: analog6#Value=507.0 01:08:14 Event: analog7#Value=511.0 01:08:15 Event: analog6#Value=507.0 01:08:16 Event: analog7#Value=511.0 01:08:17 Event: analog6#Value=507.0 01:08:18 Event: analog7#Value=511.0 01:08:19 [Errno 121] Remote I/O error //automatic reset 01:08:20 Event: analog7#Value=511.0 // problem starts again 01:08:21 Event: analog6#Value=511.0 01:08:22 Event: analog7#Value=511.0 01:08:23 Event: analog6#Value=511.0

enesbcs commented 5 years ago

I found this hint: "if the Master runs too fast and sends another Wire.beginTransmission() before the last read is complete, this can lead to communications bind up as the prior event onReceive() did not complete its work and possibly corrupting the current transmission in progress." I have to rethink I2C usage in RPIEasy. Other plugins are not so affected , but with multiple PME device started with several thread paralell needs a more exact timing and maybe some global I2C handler. Work in progress.

enesbcs commented 5 years ago

Please try commit https://github.com/enesbcs/rpieasy/commit/36f9a8724fe0079fa46931a4b6b8726b737be783 I2C queueing implemented to avoid value mixing and i2c grabbing by one plugin. Runned for several minutes without errors, but needs further testing. If ProMini suddenly stops responding, than try to modify line 79 in rpiwire.py from: time.sleep(0.02) to time.sleep(0.08) than remove power from it and reconnect. May be a timing issue, but can also be a buffer overflow error at the Arduino sketch of the ProMini. (I've got a package full of 0xFF bytes from the ProMini right before the same values began to arrive)

happytm commented 5 years ago

I tested it and it is working better than before.It lasted 1 hour this time before automatic reset.I think you can close the issue at this time and in future you can try it yourself or some one else with promini and joystick.

enesbcs commented 5 years ago

Thank you very much, your package arrived to me today, and it contains ProMini and joystick also (among many other stuff), so i can do more tests. :) I am not sure if it is a reset on the ProMini, as hardware reset solves the problem, instead seems to be a lock-up.. which can be solved with a Watchdog i think. Did you use the joystick with direct connection with a 3V ProMini or it needs a Level Shifter between the two?

happytm commented 5 years ago

I used joystick directly because pro mini i always use is 3.3v version (8mhz) the one I sent you is 3.3v so no need to have level shifter. The programming board also is set on 3.3v via jumper. I do not populate 6 pins on edge of pro mini to program it but just use holes and hold it tight with ftdi adapter while loading the program and it works. For my tests I used analog pins A6 & A7 for joystick.

enesbcs commented 5 years ago

Thanks! I rewrite the MiniProExtender sketch, relocated the slow analogread() function from the ISR into the loop, i will test it with the joystick and let you know the results.

enesbcs commented 5 years ago

I think with commit https://github.com/enesbcs/rpieasy/commit/e2eebc470e29f9769c375642fae7418a3f355937 i reached end of the road. There are nothing more i can do with the original PME sketch, approximately 1hour of continous reading can be achieved maximum, before PME I2C receiver function fails, and only power cycling can fix it. So i've done a boosted sketched that can be downloaded here: https://github.com/enesbcs/ESPEasySlaves/blob/master/MiniProExtender/MiniProExtender.ino With the new sketch it is working continously for 3hours now, without error. Tested with an analog joystick (2 analog port, 1 digital, reading interval=0 all of them)

happytm commented 5 years ago

Thank you for all your efforts on this plugin. I tested it last night for few hours and it was working finefor general input output control from rpieasy. I still will be using original sketch from espeasy for my project with your original plugin from few days ago that was working fine except analog read which I do not need.My project need to control 6 digital pins on promini and I am not controlling it directly but sending extpwm commands for each 6 pins of promini via rules and at pro mini I am storing it into 7 integers (1 for pin number & 6 integers for pwm values to further process in a sketch on promini without triggering pins at pro mini. like below:

case CMD_ANALOG_WRITE: //analogWrite(port,value); triac=(port); if (port == 4) triac1 = (port); if (port == 5) triac2 = (port); if (port == 6) triac3 = (port); if (port == 7) triac4 = (port); if (port == 8) triac5 = (port); if (port == 9) triac6 = (port); dim=(value);

I tried to store values to integers from your new pro mini sketch but I was unable to do so.

At some point you can look into serial interface to pro mini instead of using I2C. I am not sure there is any code in espeasy for that.

Thanks again.

enesbcs commented 5 years ago

The serial plugin is also under development, i ran some multithreading related problems with that. I will try the analog_write hacking, if analog_write worked previously than this also hast to work. But your sketch in the first post will not work in this form, than it contains a delay() in the loop section, which will effectively kill the whole I2C processing.

happytm commented 5 years ago

Oh that's a good news. Thank you.

enesbcs commented 5 years ago

Could you send the sketch you are using, without the unnecessary portions/comments? The one that you included in the first post, has some errors with comments and something is missing, as number of "}" is not ok. The section that calculates ESPrms is commented out, isn't it necessary? This sketch is a bit cryptic to me, triac1..triac5 has no initial value as i see, but despite it, in the setup field this values used as pin numbers.. there are no way, that anything executed before setup() that assign values to them.

happytm commented 5 years ago

I am sorry I am not a programmer so I must have messed up somewhere. but following is user Costo's original sketch I am trying to use for promini:

/****\

include

define I2C_MSG_IN_SIZE 4

define I2C_MSG_OUT_SIZE 4

define CMD_DIGITAL_WRITE 1

define CMD_DIGITAL_READ 2

define CMD_ANALOG_WRITE 3

define CMD_ANALOG_READ 4

volatile uint8_t sendBuffer[I2C_MSG_OUT_SIZE]; int ESPrms; // value to be sent over I2C to ESP

void setup() { Wire.begin(0x7f); Wire.onReceive(receiveEvent); Wire.onRequest(requestEvent); }

/* ** This part in the loop found here: [url] https://forum.arduino.cc/index.php?topic=179541.0 [/url] tnx to dc42 constant 75.7576 depends on the sensitivity of the ACS712 module Sensitivity Min<Typ<Max mV/A for 30A: 64< 66< 68 mV/A > constant = 5/.066 = 75.76 for 10A: 96<100<104 mV/A > constant = 5/.1 = 50.00 for 5A: 180<185<190 mV/A > constant = 5/.185 = 27.03 */

const int currentPin = A7; // ADC pin used for ACS712 const unsigned long sampleTime = 100000UL; // sample over 100ms, it is an exact number of cycles for both 50Hz and 60Hz mains const unsigned long numSamples = 250L; // number of samples 400 microsecond each const unsigned long sampleInterval = sampleTime/numSamples; // sampling interval, must be longer than then ADC conversion time int adc_zero = 514; // relative zero of the ACS712 for me 514 was best. 511 int valueRead; // value read on ADC

void loop() { unsigned long currentAcc = 0; unsigned int count = 0; unsigned long prevMicros = micros() - sampleInterval ;

while (count < numSamples) { if (micros() - prevMicros >= sampleInterval) { long adc_raw = analogRead(currentPin) - adc_zero; currentAcc += (unsigned long)(adc_raw * adc_raw); ++count; prevMicros += sampleInterval; } }

float rms = sqrt((float)currentAcc/(float)numSamples) (27.03 / 1024.0); // see note above for this 27.03 value ESPrms = 1000rms; // conversion of float Ampere into integer milliAmpere needed for I2C communication } /* ***

void receiveEvent(int count) { if (count == I2C_MSG_IN_SIZE) { byte cmd = Wire.read(); byte port = Wire.read(); int value = Wire.read(); value += Wire.read()*256; switch(cmd) { case CMD_DIGITAL_WRITE: pinMode(port,OUTPUT); digitalWrite(port,value); break; case CMD_DIGITAL_READ: pinMode(port,INPUT_PULLUP); clearSendBuffer(); sendBuffer[0] = digitalRead(port); break; case CMD_ANALOG_WRITE: analogWrite(port,value); break; case CMD_ANALOG_READ: clearSendBuffer(); if (port <= 4) valueRead = analogRead(port); // port <=4 to read analog value A0,A1,A2,A3 - A4 & A5 are I2C if (port == 100) valueRead = ESPrms; // port is any number >4 up to 255 sendBuffer[0] = valueRead & 0xff; sendBuffer[1] = valueRead >> 8; break; } } }

void clearSendBuffer() { for(byte x=0; x < sizeof(sendBuffer); x++) sendBuffer[x]=0; }

void requestEvent() { Wire.write((const uint8_t*)sendBuffer,sizeof(sendBuffer)); }

Also for AC dimmers I wanted to use this original sketch which is below.I was trying to use input from RPIEasy and save it into integers at pro mini and then use those values to fire triacs. :

/* AC Light Control

Ryan McLaughlin ryanjmclaughlin@gmail.com

The hardware consists of an Triac to act as an A/C switch and an opto-isolator to give us a zero-crossing reference. The software uses two interrupts to control dimming of the light. The first is a hardware interrupt to detect the zero-cross of the AC sine wave, the second is software based and always running at 1/128 of the AC wave speed. After the zero-cross is detected the function check to make sure the proper dimming level has been reached and the light is turned on mid-wave, only providing partial current and therefore dimming our AC load.

Thanks to http://www.andrewkilpatrick.org/blog/?page_id=445 and http://www.hoelscher-hi.de/hendrik/english/dimmer.htm */

/* Modified by Mark Chester mark@chesterfamily.org

to use the AC line frequency (half-period) as a reference point and fire the triacs based on that plus a count of dimming steps. Tracks the line frequency and adjusts accordingly. Can set up to an estimated 512 steps of dimmer resolution.

*/

include

include // http://www.arduino.cc/playground/Code/Timer1

int dim1; // Dimming level (0-128) 0 = on, 128 = 0ff int dim2; // Dimming level (0-128) 0 = on, 128 = 0ff int dim3; // Dimming level (0-128) 0 = on, 128 = 0ff int dim4; // Dimming level (0-128) 0 = on, 128 = 0ff //int dim5; // Dimming level (0-128) 0 = on, 128 = 0ff

int triac1; // Output to Opto Triac1 int triac2; // Output to Opto Triac2 int triac3; // Output to Opto Triac3 int triac4; // Output to Opto Triac4 //int triac5; // Output to Opto Triac5

// General unsigned long int ZeroXTime[4] = {0,0,0,0}; // Timestamp in micros() of the zero crossing interrupts unsigned long int DimStep; // How many micros() in each step of dimming unsigned long int AvgPeriod; // The average line voltage period in micros() unsigned long int PeriodResync = 3000; // Number of milliseconds between line freq measurements unsigned long int ResetPeriod = PeriodResync; // The timestamp in millis() when we will measure the period again unsigned long int DimRes = 256; // How many steps of dimmer resolution volatile unsigned long int DimStepCounter; // For counting Timer1 interrupts volatile unsigned long int FireTriac[4] = {0,0,0,0}; // When it's OK to fire the triacs, in counts of DimRes volatile boolean zero_cross = 0; // Tels us we've crossed the zero line //byte TriacPin[4] = {4,5,6,7}; // Which digital IO pins to use byte TriacPin[4] = {triac1,triac2,triac3,triac4}; // Which digital IO pins to use

define I2C_MSG_IN_SIZE 4

define I2C_MSG_OUT_SIZE 4

define CMD_DIGITAL_WRITE 1

define CMD_DIGITAL_READ 2

define CMD_ANALOG_WRITE 3

define CMD_ANALOG_READ 4

volatile uint8_t sendBuffer[I2C_MSG_OUT_SIZE];

void setup() { // Begin setup Wire.begin(0x3f); Wire.onReceive(receiveEvent); Wire.onRequest(requestEvent); Timer1.initialize(DimStep); // Start up the Timer1 timer attachInterrupt(0, zero_cross_detect, FALLING); // Attach an Interupt to Pin 2 (interupt 0) for Zero Cross Detection pinMode(triac1, OUTPUT); // Set the Triac pin as output pinMode(triac2, OUTPUT); // Set the Triac pin as output pinMode(triac3, OUTPUT); // Set the Triac pin as output pinMode(triac4, OUTPUT); // Set the Triac pin as output measure_half_period(); // Initially measure the half period } // End setup

void measure_half_period() { zero_cross = 0; // Clearing this here increases the accuracy of the measurement byte F = 0; // Frequency counter counter ;) while ( F < 4 ) { // This loop takes 4 zero cross samples if ( zero_cross ) { // Only run if a zero cross is detected ZeroXTime[F] = micros(); // Set the new current zero cross time in micros() zero_cross = 0; // Reset zero_cross F++; // Bump the counter for the next sample } } // Now we calc the length of each DimStep DimStep = (((ZeroXTime[1]-ZeroXTime[0]) + (ZeroXTime[2]-ZeroXTime[1]) + (ZeroXTime[3]-ZeroXTime[2])) / 3) / DimRes; Timer1.attachInterrupt(fire_triacs, DimStep); // (Re)Associate fire_triacs() with the Timer1 interrupt and the latest DimStep period ResetPeriod = ResetPeriod + PeriodResync; // Set the next time when we'll measure the half period again }

void zero_cross_detect() { // function to be fired at the zero crossing zero_cross = 1; // set a variable that's picked up later DimStepCounter = 0; // Reset the step counter for the next round of triac firings }

void fire_triacs() { // Called every DimStep (Timer1 interrupt, checks FireTriac[n] and fires if it's time if ( FireTriac[0] == DimStepCounter ) { // Is it time to fire? digitalWrite(triac1, HIGH); // Fire the Triac mid-phase delayMicroseconds(2); digitalWrite(triac1, LOW); // Turn off the Triac gate (Triac will not turn off until next zero cross) } if ( FireTriac[1] == DimStepCounter ) { // Is it time to fire? digitalWrite(triac2, HIGH); // Fire the Triac mid-phase delayMicroseconds(2); digitalWrite(triac2, LOW); // Turn off the Triac gate (Triac will not turn off until next zero cross) } if ( FireTriac[2] == DimStepCounter ) { // Is it time to fire? digitalWrite(triac3, HIGH); // Fire the Triac mid-phase delayMicroseconds(2); digitalWrite(triac3, LOW); // Turn off the Triac gate (Triac will not turn off until next zero cross) } if ( FireTriac[3] == DimStepCounter ) { // Is it time to fire? digitalWrite(triac4, HIGH); // Fire the Triac mid-phase delayMicroseconds(2); digitalWrite(triac4, LOW); // Turn off the Triac gate (Triac will not turn off until next zero cross) } DimStepCounter++; // This counter increments every time fire_triacs runs }

void loop() { // Main Loop if ( millis() >= ResetPeriod ) { // Measure the half period every PeriodResync milliseconds to prevent drift measure_half_period(); } FireTriac[0] = (DimRes dim1) / 1024; // Read input and calc the next triac fire time FireTriac[1] = (DimRes dim2) / 1024; // Read input and calc the next triac fire time FireTriac[2] = (DimRes dim3) / 1024; // Read input and calc the next triac fire time FireTriac[3] = (DimRes dim4) / 1024; // Read input and calc the next triac fire time

//FireTriac[2] = (DimRes analogRead(2)) / 1024; // Read input and calc the next triac fire time //FireTriac[3] = (DimRes analogRead(3)) / 1024; // Read input and calc the next triac fire time }

void receiveEvent(int count) { if (count == I2C_MSG_IN_SIZE) { byte cmd = Wire.read(); byte port = Wire.read(); int value = Wire.read(); value += Wire.read()*256; switch(cmd) { case CMD_DIGITAL_WRITE: pinMode(port,OUTPUT); digitalWrite(port,value); break; case CMD_DIGITAL_READ: pinMode(port,INPUT_PULLUP); clearSendBuffer(); sendBuffer[0] = digitalRead(port); break; case CMD_ANALOG_WRITE: //analogWrite(port,value); //triac=(port); if (port == 4) triac1 = (port); if (port == 5) triac2 = (port);
if (port == 6) triac3 = (port); if (port == 7) triac4 = (port); //if (port == 8) triac5 = (port); //dim=(value); //dim=dim / 8; if (port == 4) dim1 = (value); if (port == 5) dim2 = (value);
if (port == 6) dim3 = (value); if (port == 7) dim4 = (value); //if (port == 8) dim5 = (value); break; case CMD_ANALOG_READ: clearSendBuffer(); if (port <= 4) valueRead = analogRead(port); // port <=4 to read analog value A0,A1,A2,A3 - A4 & A5 are I2C if (port == 100) valueRead = ESPrms; // port is any number >4 up to 255 //if (port == 2) selector = analogRead(A2); //if (port == 3) level = analogRead(A3); sendBuffer[0] = valueRead & 0xff; sendBuffer[1] = valueRead >> 8; break; } } }

void clearSendBuffer() { for(byte x=0; x < sizeof(sendBuffer); x++) sendBuffer[x]=0; }

void requestEvent() { Wire.write((const uint8_t*)sendBuffer,sizeof(sendBuffer)); }

I also is planning to use following rules in RPIEasy Rules tab and I tested it with pro mini with your original Promini plugin and it was storing dimmer values and triac value in integers at pro mini fine.

4 AC dimmers with 8 (128 x 8 = 1024 ) dimming level control menu for Raspberry pi using apds9960 gesture sensor with pro mini extender plugin on pi and slave code on pro mini

on 9960#Gesture do if [9960#Gesture]=10 TaskValueSet,12,1,[dimmer#pin]+1 if [dimmer#pin]>9 TaskValueSet,12,1,9 # set higher limit of GPIO pin number for 4 dimmers if [9960#Gesture]=20 TaskValueSet,12,1,[dimmer#pin]-1 if [dimmer#pin]<6 TaskValueSet,12,1,6 # set lower limit of GPIO pin number for 4 dimmers if [9960#Gesture]=30 TaskValueSet,12,2,[dimmer#dimvalue]-128 if [dimmer#dimvalue]<0 TaskValueSet,12,2,0 # set lower limit for pwm value if [9960#Gesture]=40 TaskValueSet,12,2,[dimmer#dimvalue]+128 if [dimmer#dimvalue]>1000 TaskValueSet,12,2,1000 # set upper limit for pwm value if [9960#Gesture]=50 TaskValueSet,12,2,1000 # turn on dimmer at full power if [9960#Gesture]=60 TaskValueSet,12,2,0 # turn off dimmer endon

on dimmer#pin do Publish %sysname%/dimmer/pin,[dimmer#pin]

endon

on dimmer#dimvalue do Publish %sysname%/dimmer/dimvalue,[dimmer#dimvalue] EXTPWM,[dimmer#pin],[dimmer#dimvalue] # Send PIN and PWM value to promini (stored pwm values in 4 different integers at promini for each Pin via I2C if used with PME (pro mini extender) plugin oledcmd,clear oledcmd,on oled,6,1,Fan#[dimmer#pin]=[dimmer#dimvalue]

endon

Following are rules I use for esp8266 (espeasy) device for control using MPR121 sensor:

4 channel dimmer control

on key#press=17.00 do // MPR121 up key press for dimmer 1 TaskValueSet,12,1,[dummy#d1]+127 // Set dummy sensor 1 value if [dummy#d1]>1000 TaskValueSet,12,1,1000 endif EXTPWM,6,[dummy#d1] // set pro mini pin 6 pwm value to dummy sensor 1 value oled,2,4,FAN1=[dummy#d1] // Set oled line 2 & letter 4 to dummy sensor 1 value endon

on key#press=272.00 do // MPR121 down key press for dimmer 1 TaskValueSet,12,1,[dummy#d1]-128 if [dummy#d1]<0 TaskValueSet,12,1,0 endif EXTPWM,6,[dummy#d1] oled,2,4,FAN1=[dummy#d1] endon

on key#press=34.00 do TaskValueSet,12,2,[dummy#d2]+128 if [dummy#d2]>1000 TaskValueSet,12,2,1000 endif EXTPWM,7,[dummy#d2] oled,2,4,FAN2=[dummy#d2] endon

on key#press=544.00 do TaskValueSet,12,2,[dummy#d2]-128 if [dummy#d2]<0 TaskValueSet,12,2,0 endif EXTPWM,7,[dummy#d2] oled,2,4,FAN2=[dummy#d2] endon

on key#press=68.00 do TaskValueSet,12,3,[dummy#d3]+128 if [dummy#d3]>1000 TaskValueSet,12,3,1000 endif EXTPWM,8,[dummy#d3] oled,2,4,FAN3=[dummy#d3] endon

on key#press=1088.00 do TaskValueSet,12,3,[dummy#d3]-128 if [dummy#d3]<0 TaskValueSet,12,3,0 endif EXTPWM,8,[dummy#d3] oled,2,4,FAN3=[dummy#d3] endon

on key#press=136.00 do TaskValueSet,12,4,[dummy#d4]+128 if [dummy#d4]>1000 TaskValueSet,12,4,1000 endif EXTPWM,9,[dummy#d4] oled,2,4,FAN4=[dummy#d4] endon

on key#press=2176.00 do TaskValueSet,12,4,[dummy#d4]-128 if [dummy#d4]<0 TaskValueSet,12,4,0 endif EXTPWM,9,[dummy#d4] oled,2,4,FAN4=[dummy#d4] endon

on dimmer1 do // when http://192.168.0.73/control?cmd=event,dimmer1=100 command issued from browser TaskValueSet 12,1,%eventvalue% // store pwm level value of 0 to 1023 from above command into dummy sensor oled,2,4,FAN1=[dummy#d1] endon

on dimmer2 do TaskValueSet 12,2,%eventvalue% oled,2,4,FAN2=[dummy#d2] endon

on dimmer3 do TaskValueSet 12,3,%eventvalue% oled,2,4,FAN3=[dummy#d3] endon

on dimmer4 do TaskValueSet 12,4,%eventvalue% oled,2,4,FAN4=[dummy#d4] endon

I hope this is not too confusing.

Thanks.

enesbcs commented 5 years ago

It was a great challange to me, but now they compiles at least... https://github.com/enesbcs/ESPEasySlaves/blob/master/MiniProExtenderAC/MiniProExtenderAC.ino https://github.com/enesbcs/ESPEasySlaves/blob/master/MiniProExtenderACS/MiniProExtenderACS.ino

happytm commented 5 years ago

I tested your sketches but did not work for me so I created following sketch & used latest plugin which work perfect for me. I think finally you have resolved this issue . I watched serial console on pro mini and it is changing integer values properly. Also I was getting proper values in to rpieasy for 3 analog values from pro mini. I very much appreciate your effort. Also can you commit following sketch for future reference before I mess it up again.

/****\

/* AC Light Control

Updated by Robert Twomey rtwomey@u.washington.edu

Changed zero-crossing detection to look for RISING edge rather than falling. (originally it was only chopping the negative half of the AC wave form).

Also changed the dim_check() to turn on the Triac, leaving it on until the zero_cross_detect() turn's it off.

Ryan McLaughlin ryanjmclaughlin@gmail.com

The hardware consists of an Triac to act as an A/C switch and an opto-isolator to give us a zero-crossing reference. The software uses two interrupts to control dimming of the light. The first is a hardware interrupt to detect the zero-cross of the AC sine wave, the second is software based and always running at 1/128 of the AC wave speed. After the zero-cross is detected the function check to make sure the proper dimming level has been reached and the light is turned on mid-wave, only providing partial current and therefore dimming our AC load.

Thanks to http://www.andrewkilpatrick.org/blog/?page_id=445 and http://www.hoelscher-hi.de/hendrik/english/dimmer.htm

*/

include

include // Avaiable from http://www.arduino.cc/playground/Code/Timer1

define I2C_MSG_IN_SIZE 4

define I2C_MSG_OUT_SIZE 4

define CMD_DIGITAL_WRITE 1

define CMD_DIGITAL_READ 2

define CMD_ANALOG_WRITE 3

define CMD_ANALOG_READ 4

//#define CMD_ASK_MAX_FUNC 0x10

//volatile byte I2CReceived = 0; //volatile byte I2CDataReady = 0; volatile uint8_t sendBuffer[I2C_MSG_OUT_SIZE]; //uint8_t EmptBuffer[I2C_MSG_OUT_SIZE];

//volatile int value = 0; //volatile byte port = 0; //volatile byte cmd = 0; int ESPrms=0; // value to be sent over I2C to ESP

int triac; int dim;

volatile int i=0; // Variable to use as a counter volatile boolean zero_cross=0; // Boolean to store a "switch" to tell us if we have crossed zero

int triac1; // Output to Opto Triac1 int triac2; // Output to Opto Triac2 int triac3; // Output to Opto Triac3 int triac4; // Output to Opto Triac4 int triac5; // Output to Opto Triac5 int triac6; // Output to Opto Triac6

int LED = 10; // LED for testing

int dim1; // Dimming level (0-128) 0 = on, 128 = 0ff int dim2; // Dimming level (0-128) 0 = on, 128 = 0ff int dim3; // Dimming level (0-128) 0 = on, 128 = 0ff int dim4; // Dimming level (0-128) 0 = on, 128 = 0ff int dim5; // Dimming level (0-128) 0 = on, 128 = 0ff int dim6; // Dimming level (0-128) 0 = on, 128 = 0ff

int freqStep = 65; // or 78 based on power supply : This is the delay-per-brightness step in microseconds. // It is calculated based on the frequency of your voltage supply (50Hz or 60Hz) // and the number of brightness steps you want. // // The only tricky part is that the chopper circuit chops the AC wave twice per // cycle, once on the positive half and once at the negative half. This meeans // the chopping happens at 120Hz for a 60Hz supply or 100Hz for a 50Hz supply.

// To calculate freqStep you divide the length of one full half-wave of the power // cycle (in microseconds) by the number of brightness steps. // // (1000000 uS / 120 Hz) / 128 brightness steps = 65 uS / brightness step or (1000000 uS / 100 Hz) / 128 brightness steps = 78 uS / brightness step // // 1000000 us / 120 Hz = 8333 uS, length of one half-wave or 100 Hz =

void setup() { // for (byte x = 0; x < sizeof(sendBuffer); x++) // sendBuffer[x] = 0; // for (byte x = 0; x < sizeof(EmptBuffer); x++) // EmptBuffer[x] = 0xff; Wire.begin(0x3f); Wire.onReceive(receiveEvent); Wire.onRequest(requestEvent); Serial.begin(9600);

pinMode(triac1, OUTPUT); // Set the Triac1 pin as output pinMode(triac2, OUTPUT); // Set the Triac2 pin as output pinMode(triac3, OUTPUT); // Set the Triac3 pin as output pinMode(triac4, OUTPUT); // Set the Triac4 pin as output pinMode(triac5, OUTPUT); // Set the Triac5 pin as output pinMode(triac6, OUTPUT); // Set the Triac6 pin as output */

pinMode(LED, OUTPUT); // Set the LED pin as output

attachInterrupt(0, zero_cross_detect, RISING); // Attach an Interupt to Pin 2 (interupt 0) for Zero Cross Detection Timer1.initialize(freqStep); // Initialize TimerOne library for the freq we need Timer1.attachInterrupt(dim_check, freqStep);
// Use the TimerOne Library to attach an interrupt // to the function we use to check to see if it is // the right time to fire the triac. This function // will now run every freqStep in microseconds. }

/* ** This part in the loop found here: [url] https://forum.arduino.cc/index.php?topic=179541.0 [/url] tnx to dc42 constant 75.7576 depends on the sensitivity of the ACS712 module Sensitivity Min<Typ<Max mV/A for 30A: 64< 66< 68 mV/A > constant = 5/.066 = 75.76 for 10A: 96<100<104 mV/A > constant = 5/.1 = 50.00 for 5A: 180<185<190 mV/A > constant = 5/.185 = 27.03 */

const int currentPin = A0; // ADC pin used for ACS712 const unsigned long sampleTime = 100000UL; // sample over 100ms, it is an exact number of cycles for both 50Hz and 60Hz mains const unsigned long numSamples = 250L; // number of samples 400 microsecond each const unsigned long sampleInterval = sampleTime/numSamples; // sampling interval, must be longer than then ADC conversion time int adc_zero = 514; // relative zero of the ACS712 for me 514 was best. 511 int valueRead;

void zero_cross_detect() {
zero_cross = true; // set the boolean to true to tell our dimming function that a zero cross has occured i=0; // since the control pin stays high, the TRIAC won't 'unlatch' // when zero-crossing, so I need to put the pins to LOW digitalWrite(triac1, LOW); digitalWrite(triac2, LOW); digitalWrite(triac3, LOW); digitalWrite(triac4, LOW); digitalWrite(triac5, LOW); digitalWrite(triac6, LOW);

// writing pins is like 10 times faster if // we write the register directly // instead of using 'digitalWrite'

}

// Turn on the TRIAC at the appropriate time void dim_check() {
if(zero_cross == true) {

if(i>=dim1)  {                     
  digitalWrite(triac1, HIGH);  // turn on triac1
  i=0;  // reset time step counter                         
  zero_cross = false; //reset zero cross detection
}   

  if(i>=dim2) {                     
  digitalWrite(triac2, HIGH);  // turn on triac2 

  i=0;  // reset time step counter                         
  zero_cross = false; //reset zero cross detection
}   

  if(i>=dim3) {                     
  digitalWrite(triac3, HIGH);  // turn on triac3

  i=0;  // reset time step counter                         
  zero_cross = false; //reset zero cross detection
}   

 if(i>=dim4) {                     
  digitalWrite(triac4, HIGH);  // turn on triac4

  i=0;  // reset time step counter                         
  zero_cross = false; //reset zero cross detection
}   

if(i>=dim5) {
digitalWrite(triac5, HIGH); // turn on triac5

  i=0;  // reset time step counter                         
  zero_cross = false; //reset zero cross detection
}   

  if (i>=dim6) {                     
  digitalWrite(triac6, HIGH);  // turn on triac6
  i=0;  // reset time step counter                         
  zero_cross = false; //reset zero cross detection
}    

else {

  i++; // increment time step counter 

}

}                                

}

// void handleI2C()

void loop() { if (triac == 4) { dim1=dim;

      }
     if (triac == 5) {
        dim2=dim; 
      }

      if (triac == 6) {
        dim3=dim; 
      }
     if (triac == 7) {
        dim4=dim; 
      }

     if (triac == 8) {
        dim5=dim; 
      }

      if (triac == 9) {
        dim6=dim; 
      }

      dim_check();

//dim = dim / 8; // set dimmer value to global integer dim //dim = analogRead(POT_pin) / 8; // read dimmer value from potentiometer analogWrite(LED, dim); // write dimmer value to the LED, for debugging

Serial.print("dim = "); Serial.print(dim);

//Serial.print("triac = "); //Serial.print(triac);

Serial.print(" t1 = "); Serial.print(triac1); Serial.print(" d1 = "); Serial.print(dim1); Serial.print(" t2 = "); Serial.print(triac2); Serial.print(" d2 = "); Serial.print(dim2); Serial.print(" t3 = "); Serial.print(triac3); Serial.print(" d3 = "); Serial.print(dim3); Serial.print(" t4 = "); Serial.print(triac4); Serial.print(" d4 = "); Serial.print(dim4); Serial.print(" t5 = "); Serial.print(triac5); Serial.print(" d5 = "); Serial.print(dim5); Serial.print(" t6 = "); Serial.print(triac6); Serial.print(" d6 = "); Serial.print(dim6);

// Serial.println(" ESPrms = "); // Serial.print(ESPrms);

delay(10000); Serial.print('\n');

}

void receiveEvent(int count) { if (count == I2C_MSG_IN_SIZE) { byte cmd = Wire.read(); byte port = Wire.read(); int value = Wire.read(); value += Wire.read()*256; switch(cmd) { case CMD_DIGITAL_WRITE: pinMode(port,OUTPUT); digitalWrite(port,value); break; case CMD_DIGITAL_READ: pinMode(port,INPUT_PULLUP); clearSendBuffer(); sendBuffer[0] = digitalRead(port); break; case CMD_ANALOG_WRITE: //analogWrite(port,value); triac=(port); dim=(value); if (port == 4) triac1 = (port); if (port == 5) triac2 = (port);
if (port == 6) triac3 = (port); if (port == 7) triac4 = (port); if (port == 8) triac5 = (port); if (port == 9) triac6 = (port);
break;

    case CMD_ANALOG_READ:

    if (port <= 4) valueRead = analogRead(port); // port <=4 to read analog value A0,A1,A2,A3 - A4 & A5 are I2C
   // if (port > 79) valueRead = ESPrms; // port is any number >4 up to 255
    sendBuffer[0] = valueRead & 0xff;
    sendBuffer[1] = valueRead >> 8;

    break;
  }

} }

void clearSendBuffer() { for(byte x=0; x < sizeof(sendBuffer); x++) sendBuffer[x]=0; }

void requestEvent() { Wire.write((const uint8_t*)sendBuffer,sizeof(sendBuffer)); }

Thank you.

enesbcs commented 5 years ago

I can add it, but this sketch is using the same old I2C handling, which can lead a lockup because analogread() function located are in the receiveEvent block which is an ISR function. I am afraid that after several hour it will come about.

enesbcs commented 5 years ago

Uploaded to https://github.com/enesbcs/ESPEasySlaves/tree/master/MiniProExtenderOACS In case of further bugs, please open a new issue.