Enabling use of processor in PWM expansion module
Enabling use of processor in PWM expansion module
Hi All-
So I purchased the PWM module and find that it has a processor in it identical to the main board in the reef angel, and further that it is possible to enable direct loading of PDE files to that processor to off load some of the work from the main controller, and YOU GET double the memory, sorta. I had been corresponding with Roberto about this possibility and it seemed like a good idea to get this onto the forums. So here we go!
Proviso #1- I am very new to this and may in fact be an excellent test idiot to best ask questions about how to do this and try to work it out, likely with more than a little help from the experts. Since I am aware of at least one individual who is all ready running this way I will open it up with a simple question.
HOW can I do this.
I have Arduino and RAgen loaded, I can communicate with my controller and upload code, modify it (VERY simply) and see the effects on the controller so I am base line functional in terms of using the controller with the computer. My Controller is sitting on my living room table or in the garage as I finish the LED build (any day now) and thus I can do stupid things and not worry about a tank. I will be ordering a WiFi unit in a few days but for now I have just the controller and the PWM expansion module.
Let the fun begin!
There is one bit of information in a slightly off topic thread here... http://forum.reefangel.com/viewtopic.ph ... 4555#p4555 but I started this to get something in one place that hopefully everyone can use and find easily. THis is pretty cool.
So I purchased the PWM module and find that it has a processor in it identical to the main board in the reef angel, and further that it is possible to enable direct loading of PDE files to that processor to off load some of the work from the main controller, and YOU GET double the memory, sorta. I had been corresponding with Roberto about this possibility and it seemed like a good idea to get this onto the forums. So here we go!
Proviso #1- I am very new to this and may in fact be an excellent test idiot to best ask questions about how to do this and try to work it out, likely with more than a little help from the experts. Since I am aware of at least one individual who is all ready running this way I will open it up with a simple question.
HOW can I do this.
I have Arduino and RAgen loaded, I can communicate with my controller and upload code, modify it (VERY simply) and see the effects on the controller so I am base line functional in terms of using the controller with the computer. My Controller is sitting on my living room table or in the garage as I finish the LED build (any day now) and thus I can do stupid things and not worry about a tank. I will be ordering a WiFi unit in a few days but for now I have just the controller and the PWM expansion module.
Let the fun begin!
There is one bit of information in a slightly off topic thread here... http://forum.reefangel.com/viewtopic.ph ... 4555#p4555 but I started this to get something in one place that hopefully everyone can use and find easily. THis is pretty cool.
Re: Enabling use of processor in PWM expansion module
I will start with this. Based upon the link provided above, it looks like I just unscrew the back of the PWM module and plug in the cable and load code identical to the head unit. Ok..
So once that happens I am assuming that the PWM module runs completely independently of the Main. But I am thinking they have to be able to talk. So. How does this work. Can you place code into the Main (i.e. CONTROLLER) that triggers and event in the PWM or will the PWM processor just go along on with the PDE its using and "ignore" the main. I can see how this would get complicated so whats the best way. A similar issue... is there a master clock? Does the PWM processor have its own time... could be fun if they were different... in a bad way....
Trying to figure out whats going to happen. I am certain I can get the code loaded I just want to know what type of monster I will create in doing so.
So once that happens I am assuming that the PWM module runs completely independently of the Main. But I am thinking they have to be able to talk. So. How does this work. Can you place code into the Main (i.e. CONTROLLER) that triggers and event in the PWM or will the PWM processor just go along on with the PDE its using and "ignore" the main. I can see how this would get complicated so whats the best way. A similar issue... is there a master clock? Does the PWM processor have its own time... could be fun if they were different... in a bad way....
Trying to figure out whats going to happen. I am certain I can get the code loaded I just want to know what type of monster I will create in doing so.
- jsclownfish
- Posts: 375
- Joined: Mon Oct 24, 2011 7:52 pm
- Location: Saint Louis
Re: Enabling use of processor in PWM expansion module
I've playing along thee lines as well, but with an arduino UNO adapted to measure other things and sending the data to the master controller. http://forum.reefangel.com/viewtopic.php?f=19&t=493. It seems to be working and now I am getting ready to put into use on the tank. However, I only have a couple months more experience than you and none of the work I've done would have been possible wihtout the expertise on this forum. If I understand the PWM module correctly (I use MH lighting) it already carries a fair amount of code for the various lighting regimes that the LED systems are capable of already.
Re: Enabling use of processor in PWM expansion module
Both modules will shared the same clock, so they can synchronize things.
In theory, yes they are working independently of one another, but they can talk to each other.
They use the I2C protocol to talk to one another.
Here is some more info about the subject:
http://www.arduino.cc/en/Tutorial/MasterReader
http://www.arduino.cc/cgi-bin/yabb2/YaB ... 1248239313
http://digitalcave.ca/resources/avr/arduino-i2c.jsp
http://www.instructables.com/id/I2C-between-Arduinos/
So, what we are basically doing is multi-master configuration.
In this type of approach, both units can initiate a request and both can receive a request, and thus enabling us to have a 2-way communication with one another.
The preloaded code that goes with the PWM expansion module only does 1-way.
So, let's start by loading a very simple code to the PWM expansion module for 1-way just so we can understand what is going on.
Here is the code you will want to load into your PWM expansion module:
In this code, we have 4 major functions:
setup() - Initializes the module
loop() - Checks to see if RA sent anything. If it did, executes ProcessCMD()
receiveEvent() - This function is executed anytime the module receives data. If data is received, it assigns to cmdnum and datanum variables. Just to ensure that data is actually coming from RA, we compare the first 3 bytes to see if they are $$$, which is what RA is programmed to send to the PWM expansion module when it wants to send data. Because this function is interrupt driven, you really want to get out of this function as fast as you can and let the loop() function take care of processing anything that needs to be processed.
Then, on the RA side, you can load this very simple code to test and see if we are talking to the PWM module correctly:
This RA code assumes you have the following in your ReefAngel_Feature.h file:
The above code is going to make channel 0 of your PWM Expansion module go to 100%. The value is a number from 0 to 255, where 255 represents 100%. To make it easier, you could use 100*2.55 instead of 255.
You can check it with a voltimeter if you don't have any drivers connected to your module yet.
Now, I want to show you what's behind the scenes. The function Expansion() is defined on ReefAngel_PWM libraries, but here is a copy of it:
As you can see, we are sending the 5 bytes, which the first 3 are $$$, followed by the cmd and data values.
I hope this helped clarify a bit how they talk to each other.
2-way communication to follow...
In theory, yes they are working independently of one another, but they can talk to each other.
They use the I2C protocol to talk to one another.
Here is some more info about the subject:
http://www.arduino.cc/en/Tutorial/MasterReader
http://www.arduino.cc/cgi-bin/yabb2/YaB ... 1248239313
http://digitalcave.ca/resources/avr/arduino-i2c.jsp
http://www.instructables.com/id/I2C-between-Arduinos/
So, what we are basically doing is multi-master configuration.
In this type of approach, both units can initiate a request and both can receive a request, and thus enabling us to have a 2-way communication with one another.
The preloaded code that goes with the PWM expansion module only does 1-way.
So, let's start by loading a very simple code to the PWM expansion module for 1-way just so we can understand what is going on.
Here is the code you will want to load into your PWM expansion module:
Code: Select all
#include <Wire.h>
byte PWMports[] ={
3,5,6,9,10,11};
byte cmdnum=255;
byte datanum=255;
void setup()
{
Wire.begin(8); // I2C address 8
Wire.onReceive(receiveEvent);
for(int a=0;a<6;a++)
pinMode(PWMports[a],OUTPUT); // initialize pins as output
}
void loop()
{
if (cmdnum!=255)
{
ProcessCMD(cmdnum,datanum);
cmdnum=255;
datanum=255;
}
}
void receiveEvent(int howMany) {
if (howMany==5) // Our custom protocol is 5 bytes
{
byte cmd1, cmd2, cmd3, cmd4, cmd5;
cmd1=Wire.receive();
cmd2=Wire.receive();
cmd3=Wire.receive();
cmd4=Wire.receive();
cmd5=Wire.receive();
if (cmd1=='$' && cmd2=='$' && cmd3=='$') // the first 3 bytes of the custom protocol are $$$ to ensure it's coming from RA
{
cmdnum=cmd4; // 4th byte is the channel
datanum=cmd5; // 5th byte is the intensity
}
}
else
for (int a=0;a<howMany;a++)
Wire.receive(); // if the number of bytes is not 5, discard everything
}
void ProcessCMD(byte cmd, byte data)
{
if (cmd>=0 && cmd<=5)
analogWrite(PWMports[cmd],data); // apply the PWM value to the channel
}
setup() - Initializes the module
loop() - Checks to see if RA sent anything. If it did, executes ProcessCMD()
receiveEvent() - This function is executed anytime the module receives data. If data is received, it assigns to cmdnum and datanum variables. Just to ensure that data is actually coming from RA, we compare the first 3 bytes to see if they are $$$, which is what RA is programmed to send to the PWM expansion module when it wants to send data. Because this function is interrupt driven, you really want to get out of this function as fast as you can and let the loop() function take care of processing anything that needs to be processed.
Then, on the RA side, you can load this very simple code to test and see if we are talking to the PWM module correctly:
Code: Select all
#include <ReefAngel_Features.h>
#include <ReefAngel_Globals.h>
#include <ReefAngel_Wifi.h>
#include <Wire.h>
#include <OneWire.h>
#include <Time.h>
#include <DS1307RTC.h>
#include <ReefAngel_EEPROM.h>
#include <ReefAngel_NokiaLCD.h>
#include <ReefAngel_ATO.h>
#include <ReefAngel_Joystick.h>
#include <ReefAngel_LED.h>
#include <ReefAngel_TempSensor.h>
#include <ReefAngel_Relay.h>
#include <ReefAngel_PWM.h>
#include <ReefAngel_Timer.h>
#include <ReefAngel_Memory.h>
#include <ReefAngel.h>
void setup()
{
ReefAngel.Init(); //Initialize controller
}
void loop()
{
ReefAngel.ShowInterface();
ReefAngel.PWM.Expansion(0,255); // Send Channel 0 value 255
}
Code: Select all
#define PWMEXPANSION
You can check it with a voltimeter if you don't have any drivers connected to your module yet.
Now, I want to show you what's behind the scenes. The function Expansion() is defined on ReefAngel_PWM libraries, but here is a copy of it:
Code: Select all
void ReefAngel_PWMClass::Expansion(byte cmd, byte data)
{
Wire.beginTransmission(8); // transmit to device #8
Wire.send('$'); // send the $$$
Wire.send('$');
Wire.send('$');
Wire.send(cmd); // send the command
Wire.send(data); // send the data
Wire.endTransmission(); // stop transmitting
}
I hope this helped clarify a bit how they talk to each other.
2-way communication to follow...
Roberto.
Re: Enabling use of processor in PWM expansion module
Continuing with the code above, I've added the part that handles the other way of the communication where RA needs to request anything from the PWM module.
The code below is doing nothing more than returning 128, but it is just meant to be an example of how to do it and not practical example.
As you can see, i've added a new major function to the code:
requestEvent() - This function is called everytime RA requests anything. You may choose to return as many bytes of data you wish. In this example we are just returning a fixed number 128.
On the RA side, you can use this code to request data from the PWM Expansion module:
The code above will ask the PWM Expansion module for 1 byte of data and it will assign the returning data to the variable DataFromPWM.
You can then use the variable DataFromPWM for anything you wish within your code.
The code below is doing nothing more than returning 128, but it is just meant to be an example of how to do it and not practical example.
Code: Select all
#include <Wire.h>
byte PWMports[] ={
3,5,6,9,10,11};
byte cmdnum=255;
byte datanum=255;
void setup()
{
Wire.begin(8); // I2C address 8
Wire.onReceive(receiveEvent);
Wire.onRequest(requestEvent);
for(int a=0;a<6;a++)
pinMode(PWMports[a],OUTPUT); // initialize pins as output
}
void loop()
{
if (cmdnum!=255)
{
ProcessCMD(cmdnum,datanum);
cmdnum=255;
datanum=255;
}
}
void receiveEvent(int howMany) {
if (howMany==5) // Our custom protocol is 5 bytes
{
byte cmd1, cmd2, cmd3, cmd4, cmd5;
cmd1=Wire.receive();
cmd2=Wire.receive();
cmd3=Wire.receive();
cmd4=Wire.receive();
cmd5=Wire.receive();
if (cmd1=='$' && cmd2=='$' && cmd3=='$') // the first 3 bytes of the custom protocol are $$$ to ensure it's coming from RA
{
cmdnum=cmd4; // 4th byte is the channel
datanum=cmd5; // 5th byte is the intensity
}
}
else
for (int a=0;a<howMany;a++)
Wire.receive(); // if the number of bytes is not 5, discard everything
}
void ProcessCMD(byte cmd, byte data)
{
if (cmd>=0 && cmd<=5)
analogWrite(PWMports[cmd],data); // apply the PWM value to the channel
}
void requestEvent() {
Wire.send(128);
}
requestEvent() - This function is called everytime RA requests anything. You may choose to return as many bytes of data you wish. In this example we are just returning a fixed number 128.
On the RA side, you can use this code to request data from the PWM Expansion module:
Code: Select all
#include <ReefAngel_Features.h>
#include <ReefAngel_Globals.h>
#include <ReefAngel_Wifi.h>
#include <Wire.h>
#include <OneWire.h>
#include <Time.h>
#include <DS1307RTC.h>
#include <ReefAngel_EEPROM.h>
#include <ReefAngel_NokiaLCD.h>
#include <ReefAngel_ATO.h>
#include <ReefAngel_Joystick.h>
#include <ReefAngel_LED.h>
#include <ReefAngel_TempSensor.h>
#include <ReefAngel_Relay.h>
#include <ReefAngel_PWM.h>
#include <ReefAngel_Timer.h>
#include <ReefAngel_Memory.h>
#include <ReefAngel.h>
byte DataFromPWM;
void setup()
{
ReefAngel.Init(); //Initialize controller
}
void loop()
{
ReefAngel.ShowInterface();
Wire.requestFrom(8,1);
if (Wire.available()) DataFromPWM=Wire.receive();
}
You can then use the variable DataFromPWM for anything you wish within your code.
Roberto.
- jsclownfish
- Posts: 375
- Joined: Mon Oct 24, 2011 7:52 pm
- Location: Saint Louis
Re: Enabling use of processor in PWM expansion module
These last two posts are super helpful! Thanks for the lesson.
-Jon
-Jon
Re: Enabling use of processor in PWM expansion module
Hi Roberto-
I am now finally starting to play with all this, kinda slowly.. but one thing I wanted to clarify before I go uploading new programs to my PWM expansion board...
Will you post the PDE that is loaded on it when you ship them, so when I screw it up and it no longer works I can reload something that will make it work for baseline function
Thank you soo much for all the information, although I am sure you have provided enough to get most people up and running with it I am sure I will have more questions as I go forward.
I plan to build a test code that basically lets me set from the controller each PWM channel dim output independently, I figure this is about as easy a task as I can pick to test this type of coding. Do you see this as a reasonable starting point. Menus + 1 way comm is all I see that needs to be done.. which will be a challenge for me anyway. I can sorta just take your example and add in a menu item for each channel (neuter the rest of the controller functions to keep it SUPER simple).
I am now finally starting to play with all this, kinda slowly.. but one thing I wanted to clarify before I go uploading new programs to my PWM expansion board...
Will you post the PDE that is loaded on it when you ship them, so when I screw it up and it no longer works I can reload something that will make it work for baseline function
Thank you soo much for all the information, although I am sure you have provided enough to get most people up and running with it I am sure I will have more questions as I go forward.
I plan to build a test code that basically lets me set from the controller each PWM channel dim output independently, I figure this is about as easy a task as I can pick to test this type of coding. Do you see this as a reasonable starting point. Menus + 1 way comm is all I see that needs to be done.. which will be a challenge for me anyway. I can sorta just take your example and add in a menu item for each channel (neuter the rest of the controller functions to keep it SUPER simple).
Re: Enabling use of processor in PWM expansion module
Here is the code that goes preloaded:
To set individual channels, you can use this:
Code: Select all
#include <Wire.h>
#include <avr/wdt.h>
byte PWMports[] ={
3,5,6,9,10,11};
byte ChannelValue[] = {
0,0,0,0,0,0};
byte cmdnum=255;
byte datanum=255;
void setup()
{
Serial.begin(57600);
Wire.begin(8);
Wire.onReceive(receiveEvent);
randomSeed(analogRead(0));
pinMode(3,OUTPUT);
pinMode(5,OUTPUT);
pinMode(6,OUTPUT);
pinMode(9,OUTPUT);
pinMode(10,OUTPUT);
pinMode(11,OUTPUT);
wdt_enable(WDTO_1S);
}
void loop()
{
wdt_reset();
//Serial.println(ChannelValue[0],DEC);
if (cmdnum!=255)
{
ProcessCMD(cmdnum,datanum);
cmdnum=255;
datanum=255;
}
}
void receiveEvent(int howMany) {
wdt_reset();
if (howMany==5)
{
byte cmd1, cmd2, cmd3, cmd4, cmd5;
cmd1=Wire.receive();
cmd2=Wire.receive();
cmd3=Wire.receive();
cmd4=Wire.receive();
cmd5=Wire.receive();
if (cmd1=='$' && cmd2=='$' && cmd3=='$')
{
cmdnum=cmd4;
datanum=cmd5;
//Serial.println(cmd4,DEC);
//Serial.println(cmd5,DEC);
}
}
else
{
for (int a=0;a<howMany;a++)
{
Wire.receive();
}
}
}
void ProcessCMD(byte cmd, byte data)
{
wdt_reset();
// Individual Channel
if (cmd>=0 && cmd<=5)
{
ChannelValue[cmd]=data;
analogWrite(PWMports[cmd],data);
}
//Clouds
if (cmd==6)
{
//Serial.println(data,DEC);
for (int b=0;b<7;b++)
{
if (b<6)
{
for (int a=ChannelValue[b];a>0;a--)
{
analogWrite(PWMports[b],a);
wdt_reset();
delay(data);
}
}
if (b>0)
{
for (int a=0;a<ChannelValue[b-1];a++)
{
analogWrite(PWMports[b-1],a);
wdt_reset();
delay(data);
}
}
}
}
//Thnderstorm
if (cmd==7)
{
wdt_reset();
for(int a=0;a<6;a++)
{
analogWrite(PWMports[a],255);
}
delay(30);
for(int a=0;a<6;a++)
{
analogWrite(PWMports[a],0);
}
delay(10);
analogWrite(PWMports[0],255);
delay(100);
analogWrite(PWMports[4],0);
analogWrite(PWMports[3],0);
delay(10);
analogWrite(PWMports[0],0);
analogWrite(PWMports[3],ChannelValue[3]);
analogWrite(PWMports[1],0);
delay(60);
analogWrite(PWMports[3],0);
delay(90);
analogWrite(PWMports[4],ChannelValue[4]);
analogWrite(PWMports[1],ChannelValue[1]);
analogWrite(PWMports[2],0);
wdt_reset();
delay(100);
analogWrite(PWMports[2],255);
analogWrite(PWMports[5],255);
analogWrite(PWMports[4],0);
delay(30);
analogWrite(PWMports[5],0);
analogWrite(PWMports[0],255);
wdt_reset();
delay(300);
analogWrite(PWMports[4],ChannelValue[4]);
for(int a=0;a<6;a++)
{
analogWrite(PWMports[a],ChannelValue[a]);
}
}
//Thnderstorm Random
if (cmd==8)
{
for(int a=0;a<10;a++)
{
analogWrite(PWMports[random(6)],random(256));
analogWrite(PWMports[random(6)],0);
analogWrite(PWMports[random(6)],255);
wdt_reset();
delay(random(200));
}
delay(30);
for(int a=0;a<6;a++)
{
analogWrite(PWMports[a],ChannelValue[a]);
}
}
}
Code: Select all
ReefAngel.PWM.Expansion(0,100); //Sets channel 0 to 100%
ReefAngel.PWM.Expansion(1,34); //Sets channel 1 to 34%
ReefAngel.PWM.Expansion(2,81); //Sets channel 2 to 81%
ReefAngel.PWM.Expansion(3,12); //Sets channel 3 to 12%
ReefAngel.PWM.Expansion(4,20); //Sets channel 4 to 20%
ReefAngel.PWM.Expansion(5,73); //Sets channel 5 to 73%
Roberto.
Re: Enabling use of processor in PWM expansion module
THANKS! HAHA... EASY PEASY.. if you know what your doing. Now I just need to build a custom menu that ouputs that based upon my input string.
Re: Enabling use of processor in PWM expansion module
So I think I know this is correct but its not totally obvious to me... please let me know if this is how the PWM board works in terms of driving PWM% Values.
When we wish to output PWM channel values to the PWM expansion from code running on the PWM expansion it looks like you need to output scaled bit values of 0-255 rather than the standard PWMSlope settings which scale 0-100% when your coding on the controller, the reason I think this is true follows, please correct errors and explain if I am wrong anywhere here.
Basically, in prior posts in this thread I see
which I think indicates (cmdnum= PWM channel) (datanum=intensity) that the intensity data value must be 0-255. Whereas the Cloud.pde file that runs on the controller uses this format (see code below) to pass intensity settings to the PWM expansion which seems to scale them to 0-255
So when running PWMSlope on the PWM board, would we use something like this... to get 100% output.
PWMChannel[LEDPWM0]=PWMSlope(10,30,22,00,0,255,60,0)
Just trying to help myself understand and to be certain I don't make a mistake. If you did pass a value that was out of range, I assume the PWM board would not do much other than either go to 100% or just simply error out.
I am pretty sure this- scale 0-255 on PWM board code- is the case as code for the controller (form another topic) uses 2.55*%Int desired to set output but there was no discussion as to why that was used... and although I have looked hard for another reason why that needed to be changed, its a longer PDE and I could EASILY have missed something important that explains this and makes me wrong.
THANKS. I am really enjoying the ReefAngel and finally am starting to "get it"- its amazing really. Just last night I wired up and soldered together my UV LED string, so I needed to set output current and voltage on the driver... I just loaded up a quick PDE that allows me to set PWM output at any value- scaled the output on the controller, loaded up my cloud PDE and was back to running again in a few minutes. I don't know about other controllers but I cannot imagine how anything can compete with the ability we have here. Its totally worth the time it takes to learn the in's and out's... and I am only just beginning but loving it.
When we wish to output PWM channel values to the PWM expansion from code running on the PWM expansion it looks like you need to output scaled bit values of 0-255 rather than the standard PWMSlope settings which scale 0-100% when your coding on the controller, the reason I think this is true follows, please correct errors and explain if I am wrong anywhere here.
Basically, in prior posts in this thread I see
Code: Select all
byte PWMports[] ={
3,5,6,9,10,11};
byte cmdnum=255;
byte datanum=255;
Code: Select all
//Setup PWMSlope with values
PWMChannel[LEDPWM0]=PWMSlope(10,30,22,00,0,100,60,PWMChannel[LEDPWM0]);
//Output scaled values to PWM expansion board
PWMExpansion(LEDPWM0,int(2.55*PWMChannel[LEDPWM0]));
PWMChannel[LEDPWM0]=PWMSlope(10,30,22,00,0,255,60,0)
Just trying to help myself understand and to be certain I don't make a mistake. If you did pass a value that was out of range, I assume the PWM board would not do much other than either go to 100% or just simply error out.
I am pretty sure this- scale 0-255 on PWM board code- is the case as code for the controller (form another topic) uses 2.55*%Int desired to set output but there was no discussion as to why that was used... and although I have looked hard for another reason why that needed to be changed, its a longer PDE and I could EASILY have missed something important that explains this and makes me wrong.
THANKS. I am really enjoying the ReefAngel and finally am starting to "get it"- its amazing really. Just last night I wired up and soldered together my UV LED string, so I needed to set output current and voltage on the driver... I just loaded up a quick PDE that allows me to set PWM output at any value- scaled the output on the controller, loaded up my cloud PDE and was back to running again in a few minutes. I don't know about other controllers but I cannot imagine how anything can compete with the ability we have here. Its totally worth the time it takes to learn the in's and out's... and I am only just beginning but loving it.