Monday, June 4, 2012

RGBshow() - Super Smooth Full Color LED Fading


I was working on putting together OSCars lighting system and of course I had to get my RGB led set up first. Several months back I was looking online for a better method to fade an LED. I cant remember where I found this or I would happily give the creator credit for it.





int periode = 5000;
time = millis();
value = 128+127*cos(2*PI/periode*time);
analogWrite(ledpin, value) ;

So basically what happens is once you reach 'value =' it maps that variable to a point on a wave. As the code loops that point will follow along the wave thanks to 'time=mills();' which uses the Arduinos built in running counter to make it both smooth as well as non-blocking. 


The generated wave rises and falls symetrically and you can control the time from the start to the end of the wave by adjusting the 'periode' value. A value of 1000 will be about one second.

So with the following code  you should be able to smoothly fade one led from minimum to full brightness back to minimum in 5 seconds...


const int ledpin = 13;
long time=0;
int periode = 5000;
int led_val = 0;

void setup() {
//nothing
}

void loop() {
rgbShow();
// Im going to run it as a function for ease of integration
analogWrite(ledpin, led_val)
//we will constantly write the value thats generated from the function to the ledpin
}

void rgbShow() {
time = millis();
value = 128+127*cos(2*PI/periode*time);
led_val = value;
}

But I want to use it with a full color LED!


So were just going to add two more variables called displace and displace2, these two values will offset the start of the wave - from the start of the first wave - by a value of milliseconds.
// declare before setup
int displace = 250;
int displace2 = 500;
//....setup()
 This will allow us to generate three waves for the three colors of our led.

void rgbShow() {
time = millis();
  value = 128+127*cos(2*PI/periode*time);
  value2 = 128+127*cos(2*PI/periode*(displace-time));
  value3 = 128+127*cos(2*PI/periode*(displace2-time));
redval = value;      
greenval = value2;    
blueval = value3;      
}

 Expirement with the values and see what you get!

39 comments:

  1. Hey Paul,

    great work! Will you release the complete sketch with all the osc stuff? Its exactly what im searching for - but still stuck at the blocking for loop with delay :/
    You can mail me if you like at nibelungen79 attt gmail.com

    Best regards,
    -andreas

    ReplyDelete
  2. Sure no problem- This sketch was actually written for an UNO w/ Arduino Motor Shield, also the functions to directly control the Red, Green, and Blue are not written in this version- for the video I just changed the values of the motor pins to match the RGB pins.

    I keep the sketch separated into a few files and will post them the same way:

    ReplyDelete
  3. //**FILE1**//

    #define SOP '<'
    #define EOP '>'

    import processing.serial*;

    bool started = false;
    bool ended = false;

    char inData[80];
    byte index;



    const int dirPinA = 13;
    const int dirPinB = 12;
    const int speedPinA = 3;
    const int speedPinB = 11;
    const int breakPinA = 9;
    const int breakPinB = 8;
    const int readPinA = A0;
    const int readPinB = A1;


    int speedA = 0;
    int speedB = 0;
    int dirA = 0;
    int dirB = 0;

    int breakA = 0;
    int breakB = 0;

    const int speed_limit = 255;
    int speed_trim = 175;
    int go_speed = 0;
    int fspd_direction = 0;
    int turn_rate = go_speed*.125;


    String readString, sval, sval2, command, command_out; // used for values sent from serial in serialControl()
    int command_value = 0;
    int message = 0;


    bool report_motors = false;
    int motor_currency_a = 0;
    int motor_currency_b = 0;

    int value, value2, value3 ;

    long time=0;
    int displace = 250;
    int displace2 = 500;
    int periode = 5000;

    int rgbshow_on;



    void setup() {
    Serial.begin(19200);
    Serial.println("Hey There Buddy\n");
    pinMode (dirPinA, OUTPUT);
    pinMode (dirPinB, OUTPUT);
    pinMode (speedPinA, OUTPUT);
    pinMode (speedPinB, OUTPUT);
    pinMode (breakPinA, OUTPUT);
    pinMode (breakPinB, OUTPUT);

    }

    void loop() {


    serialControl();
    //Serial.println("finished serialControl()");



    // WRITE TO YOUR PINS HERE (THEYRE CONTINUOUSLY BEING UPDATED BASED ON THE RESULTS OF DO_ME)

    digitalWrite(dirPinA, dirA);
    digitalWrite(dirPinB, dirB);
    digitalWrite(breakPinB, breakB);
    digitalWrite(breakPinA, breakA);

    analogWrite(speedPinA, speedA);
    analogWrite(speedPinB, speedB);

    // Serial.println("finished writes");

    heyMan();


    // Serial.println("finished heyMan - End Loop");


    }

    ReplyDelete
  4. //**FILE2 - SERIAL CONTROL **//

    void serialControl() {

    // Read all serial data available, as fast as possible
    while(Serial.available() > 0)
    {
    char inChar = Serial.read();
    if(inChar == SOP)
    {
    index = 0;
    inData[index] = '\0';
    started = true;
    ended = false;



    }
    else if(inChar == EOP)
    {
    ended = true;
    break;
    }
    else
    {
    if(index < 79)
    {
    inData[index] = inChar;
    index++;
    inData[index] = '\0';
    }
    }
    }

    // We are here either because all pending serial
    // data has been read OR because an end of
    // packet marker arrived. Which is it?
    if(started && ended)
    {
    // The end of packet marker arrived. Process the packet

    // Serial.println(inData);
    // Serial.print(inData[index]);

    readString += inData; //makes the string readString
    // if (readString.length() >0) {

    sval = readString.substring(0, 4);
    sval2 = readString.substring(4, 7);
    //
    // Serial.print(sval);
    // Serial.print(sval2);

    int n1; //declare as number
    int n2;

    // char carray1[6]; //magic needed to convert string to a number
    // sval1.toCharArray(carray1, sizeof(carray1));

    char carray2[6];
    sval2.toCharArray(carray2, sizeof(carray2));
    n2 = atoi(carray2);
    command_value = n2;




    doMe();



    // Reset for the next packet
    started = false;
    ended = false;
    index = 0;
    inData[index] = '\0';
    }

    }


    ReplyDelete
  5. //**FILE3 - DO_ME **//



    void doMe () {

    /* MEAT AND POTATOS -

    Here is where we map our OSC commands to values. The IF statements
    Will match 4 character ´keys´ and their values sent by OSC

    So in your OSC controller (TouchOSC in my case) you just name the
    control something like ´trim´ - the Processing sketch will send
    that and the value which is matched here


    */


    /*
    Serial.println(sval);
    Serial.println(command_value);
    */

    if(sval == "trim") {
    speed_trim = map(command_value, 0, 255, 0, 255);
    }

    if(sval == "fbtn") {
    fspd_direction = LOW;
    }

    if(sval == "rbtn") {
    fspd_direction = HIGH;
    }

    if(sval == "read") {
    int motorcurra = analogRead(A0)*2;
    motor_currency_a = map(motorcurra, 0, 2046, 0, 1000);

    int motorcurrb = analogRead(A1)*2;
    motor_currency_b = map(motorcurrb, 0, 2046, 0, 1000);

    reportMotors();
    // if(command_value == 255) {
    // report_motors = true;
    // }
    // else if(command_value == 0) {
    // report_motors = false;
    // }
    }


    if(sval == "lfwd") { // Left wheel forward
    if(dirA == HIGH) {
    speedA = 0;
    }
    adjustSpeed();
    dirA = LOW;
    speedA = go_speed;
    }

    if(sval == "lrvs") { //left wheel reverse
    if(dirA == LOW) {
    speedA = 0;
    }
    adjustSpeed();
    dirA = HIGH;
    speedA = go_speed;
    }

    if(sval == "rfwd") { // Right wheel forward
    if(dirB == HIGH) {
    speedB = 0;
    }

    adjustSpeed();
    dirB = LOW;
    speedB = go_speed;
    }

    if(sval == "rrvs") { // Right wheel reverse
    if(dirB == LOW) {
    speedB = 0;
    }

    adjustSpeed();
    dirB = HIGH;
    speedB = go_speed;
    }

    if(sval == "rotl") { // Rotate left
    adjustSpeed();

    dirA = HIGH;
    dirB = LOW;
    speedA = go_speed;
    speedB = go_speed;
    }


    if(sval == "rotr") { // Rotate right

    adjustSpeed();
    dirA = LOW;
    dirB = HIGH;
    speedA = go_speed;
    speedB = go_speed;

    }


    if(sval == "stop") { // Full Breaking

    if(command_value == 255) {
    breakA = HIGH;
    breakB = HIGH;
    speedA = 1;
    speedB = 1;
    }

    if(command_value == 0) {
    breakA = LOW;
    breakB = LOW;
    }

    }



    if(sval == "turn"){
    if(command_value == 0){
    speedB += turn_rate;
    speedA -= turn_rate;
    }

    if(command_value == 255) {
    speedB -= turn_rate;
    speedA += turn_rate;
    }
    }


    if(sval == "fspd") {
    adjustSpeed();
    dirA = fspd_direction;
    dirB = fspd_direction;
    speedA = go_speed;
    speedB = go_speed;

    //command_out = "fspd \t";
    //command_out += String(go_speed);
    //Serial.println(command_out);
    //command_out = "";
    }

    if(sval == "fbtn") {
    fspd_direction = LOW;
    }

    if(sval == "rbtn") {
    fspd_direction = HIGH;
    }

    readString=""; // Clear readString
    }

    ReplyDelete
  6. //**FILE4-OTHER STUFF**//

    void rgbShow () {


    // for (long t = 0; t <= (periode*PI)/2; t++) {
    time = millis();
    value = 128+127*cos(2*PI/periode*time);
    value2 = 128+127*cos(2*PI/periode*(displace-time));
    value3 = 128+127*cos(2*PI/periode*(displace2-time));
    rstate = value;
    gstate = value2;
    bstate = value3;
    // }
    }


    // use these to report stuff back to serial
    // nottice the command_out lines

    void heyMan() {

    if(report_motors) {
    reportMotors();
    }
    }
    void reportMotors() {

    int sample_amount = 10;

    for(int i = 0; i < sample_amount; i++) {
    command_out = "cura";
    command_out += String(motor_currency_a) + "\n";
    command_out += "crrb";
    command_out += String(motor_currency_b);

    Serial.println(command_out);
    }


    command_out = "";
    // Serial.println("currb \t"+motor_currency_b);
    }

    ReplyDelete

  7. **************
    PROCESSING SKETCH!!
    THIS IS NOT ARDUINO CODE (well not really)
    http://processing.org/
    *****************


    import oscP5.*; // Load OSC P5 library
    import netP5.*; // Load net P5 library
    import processing.serial.*; // Load serial library

    Serial arduinoPort; // Set arduinoPort as serial connection
    OscP5 oscP5; // Set oscP5 as OSC connection

    //String command = "";
    String msg_out = "";
    String prev_msg = "";

    String incoming_command_value = "";
    String incoming_prefix = "";
    //String myOscAddr = "";


    NetAddressList myNetAddressList = new NetAddressList();
    /* listeningPort is the port the server is listening for incoming messages */
    int myListeningPort = 8000;
    /* the broadcast port is the port the clients should listen for incoming messages from the server*/
    int myBroadcastPort = 8080;

    String myConnectPattern = "/server/connect";
    String myDisconnectPattern = "/server/disconnect";
    String myAlertPattern = "/alert/";


    void setup() {
    size(325,500);
    noStroke();
    oscP5 = new OscP5(this, myListeningPort);
    arduinoPort = new Serial(this, Serial.list()[0], 19200);
    arduinoPort.bufferUntil('\n');
    // frameRate(20);
    }

    private void connect(String theIPaddress) {

    if (!myNetAddressList.contains(theIPaddress, myBroadcastPort)) {
    myNetAddressList.add(new NetAddress(theIPaddress, myBroadcastPort));
    println("### adding "+theIPaddress+" to the list.");
    } else {
    println("### "+theIPaddress+" is already connected.");
    }
    println("### currently there are "+myNetAddressList.list().size()+" remote locations connected.");
    }



    private void disconnect(String theIPaddress) {
    if (myNetAddressList.contains(theIPaddress, myBroadcastPort)) {
    myNetAddressList.remove(theIPaddress, myBroadcastPort);
    println("### removing "+theIPaddress+" from the list.");
    } else {
    println("### "+theIPaddress+" is not connected.");
    }
    println("### currently there are "+myNetAddressList.list().size());
    }



    void draw() {
    background(50);
    }

    void serialEvent (Serial Port) {
    // get the ASCII string:
    String inString = arduinoPort.readStringUntil('\n');
    if (inString != null) {
    // trim off any whitespace:
    inString = trim(inString);
    incoming_prefix = inString.substring(0, 5);
    incoming_command_value = inString.substring(7, 9);

    newOscMessage();

    }
    }

    void newOscMessage() {
    OscMessage myOscMessage = new OscMessage("/4/");
    myOscMessage.setAddrPattern("/4/"+incoming_prefix);
    myOscMessage.add(incoming_command_value);
    oscP5.send(myOscMessage, myNetAddressList);
    }


    void oscEvent(OscMessage theOscMessage) {
    if (theOscMessage.addrPattern().equals(myConnectPattern)) {
    connect(theOscMessage.netAddress().address());
    }
    else if (theOscMessage.addrPattern().equals(myDisconnectPattern)) {
    disconnect(theOscMessage.netAddress().address());
    }
    else if (theOscMessage.addrPattern().equals(myAlertPattern)) {
    disconnect(theOscMessage.netAddress().address());
    }

    else {

    try {
    oscP5.send(theOscMessage, myNetAddressList);
    } catch (Exception InvocationTarget) {}
    finally {
    String addr = theOscMessage.addrPattern();
    String list[] = split(addr,'/');
    String command = addr.substring(3,7);
    float commandval = theOscMessage.get(0).floatValue();
    int j = int(commandval*255);
    String send_package = "<"+command +j +">";
    msg_out = send_package;
    }
    try{
    arduinoPort.write(msg_out);
    } finally {
    msg_out = "";
    }

    }

    }

    ReplyDelete
  8. Thanks a lot Paul,

    didnt expect such a fast reply :) and the full code! Great!
    One question remains - i tried to get behind it, but maybe you can explain this math?
    value = 128+127*cos(2*PI/oscPeriode*time);
    so 128+127 i think is the the highest value (255) but why 128+ 127*cos
    and why PI? I didnt solve it.... Maybe you can explain this?!

    Thanks in advance!!
    -andreas

    ReplyDelete
  9. ah yes... i renamed periode to oscPeriode as i also get this value out of OSC Input :) just to be clear i edited your function.

    ReplyDelete
  10. This comment has been removed by the author.

    ReplyDelete
  11. Hey Paul,

    its me again - is there a way to force the starting value at 254 or 0 ??

    time = millis();
    value = 128+127*cos(2*PI/oscPeriode*time);
    redval = 255;
    greenval = 0;
    blueval = value;

    It always starts at a different value rises up to 254 and fall down to 0 as expected...
    Starting from 254 down to 0 or
    starting from 0 and rise up to 254
    is currently not under control by the beginning.
    Anyway to get it?

    Thanks a lot!

    ReplyDelete
  12. I think would need to setup another variable to capture the value of millis() when it starts and then subtract that from millis() in the loop.
    *and then another value if you wanted to start it at something other than 0


    startvalue = 10 // this is the value you want to start at.
    starttime = millis();

    value = 128+127*cos(2*PI/oscPeriode*(time-starttime);


    you might need to play around with where exactly you capture the starttime- depending on how your program is setup, you would want to do it right before you run the loop.

    ReplyDelete
  13. int startvalue = 175 // any number 0-255

    void loop() {

    starttime = millis();
    rgbshow(starttime)

    }

    void rgbshow(starttime) {

    time = millis();

    value = 128+127*cos(2*PI/oscPeriode*(time-(starttime+startvalue)));
    }


    ******
    I havent tested this but it seems legit

    ReplyDelete
  14. At first thanks for your great support and fast reply!!

    I tried it in my current set up with no success - so i set up a new sketch with the following:

    //initialize values
    long time = 0;
    long starttime = 0;
    int startvalue = 5; // any number 0-255
    int value = 0;
    int oscPeriode = 5000;

    void setup(){
    //start serial output
    Serial.begin(115200);
    }

    void loop() {
    //set counter base
    starttime = millis();
    //run function
    rgbshow(starttime);
    }

    void rgbshow(long starttime) {
    //set function counter base
    time = millis();
    //generate value between 0-255 start with $startvalue
    value = 128+127*cos(2*PI/oscPeriode*(time-(starttime+startvalue)));
    //print generated value
    Serial.println(value, DEC);
    }

    And the result is:

    254
    254
    254
    254
    254
    254
    254
    254
    254
    254
    254
    254
    254
    254
    254
    254
    254
    254
    254
    254
    254

    all the time. I tried to play with the position of the time values and the startvalue with no succes - changing startvaule change the result indeed but thats very clear with the math used.

    I will try some changes - but maybe you have more success and you find the wrong part faster than me.
    Honestly i still didnt get the idea behin cosinus and the PI maths in the value line :/ ...

    ReplyDelete
  15. Honestly I never quite understood the math myself (Only got as high as College Algebra II). I know it has to do with plotting a curve on a graph- might even be an EE formula for calculating frequencies?

    I always intended to learn more about it since it seems understanding a function like this could be useful in a lot of ways. Just googling right now I came across this site, might give you some insight>
    http://www.intmath.com/trigonometric-graphs/2-graphs-sine-cosine-period.php

    ReplyDelete
  16. Try this instead-

    value = 128+127*cos(2*PI/oscPeriode*time-(starttime+startvalue));


    I think I left in an extra set of parenthesis in my previous reply...

    My harddrive crashed over the weekend- so Im still trying to get everything set back up. Let me see if I can do this in Python to figure it out.

    ReplyDelete
  17. Hey Paul,

    maybe it was my mistake put the starttime into the wrong place, but the first value math was okay so far.

    Here is the result of my testing - it works for me so far with an arduino ethernet.

    Thanks again for your support and i hope you have an backup of your harddrive!


    /*
    Fade !!ANALOG!! RGB LED-Strips without delay() for nonblocking program
    rgbFadeAway fades blue->violet->red->yellow->green->teal->blue in loop
    Thanks to Paul Kelly for the value math ->
    http://electronomous.blogspot.de/2012/06/rgbshow-super-smooth-full-color-led.html
    Further informationen of the math 2*PI....
    http://www.intmath.com/trigonometric-graphs/2-graphs-sine-cosine-period.php
    CC - andyw
    nibelungen79[[at]]googlemail.com
    */

    //init VALUES
    //PIN config -change to your needs-
    uint8_t redPin = 3;
    uint8_t greenPin = 5;
    uint8_t bluePin = 6;

    //-nothing to change here-
    uint8_t redval, greenval, blueval;
    long time = 0;
    long starttime = 0;
    int startvalue = 255;
    int value = 0;
    int periode;
    int cmode = 0;

    void setup(){
    //init LED STRIP -nothing to change here-
    pinMode(redPin, OUTPUT);
    pinMode(greenPin, OUTPUT);
    pinMode(bluePin, OUTPUT);

    //setup void VALUES -change to your needs-
    periode = 50000; //time in ms from 0-255 or 0-255
    // change nothing below here
    starttime = millis();
    }

    void loop(){
    rgbFadeAway(starttime);
    analogWrite(redPin, 255-redval);
    analogWrite(greenPin, 255-greenval);
    analogWrite(bluePin, 255-blueval);
    }

    void rgbFadeAway(long starttime) {
    switch (cmode){
    case 0:
    time = millis();
    value = 128+127*cos(2*PI/periode*(time-(starttime+startvalue)));
    redval = 255-value;
    greenval = 0;
    blueval = 255;
    if( value == 2){
    cmode = 1;
    }
    break;
    case 1:
    time = millis();
    value = 128+127*cos(2*PI/periode*(time-(starttime+startvalue)));
    redval = 255;
    greenval = 0;
    blueval = 255-value;
    if( value == 253){
    cmode = 2;
    }
    break;
    case 2:
    time = millis();
    value = 128+127*cos(2*PI/periode*(time-(starttime+startvalue)));
    redval = 255;
    greenval = 255-value;
    blueval = 0;
    if( value == 2){
    cmode = 3;
    }
    break;
    case 3:
    time = millis();
    value = 128+127*cos(2*PI/periode*(time-(starttime+startvalue)));
    redval = 255-value;
    greenval = 255;
    blueval = 0;
    if( value == 253){
    cmode = 4;
    }
    break;
    case 4:
    time = millis();
    value = 128+127*cos(2*PI/periode*(time-(starttime+startvalue)));
    redval = 0;
    greenval = 255;
    blueval = 255-value;
    if( value == 3){
    cmode = 5;
    }
    break;
    case 5:
    time = millis();
    value = 128+127*cos(2*PI/periode*(time-(starttime+startvalue)));
    redval = 0;
    greenval = 255-value;
    blueval = 255;
    if( value == 253){
    cmode = 0;
    }
    break;
    }
    }

    ReplyDelete
  18. By the way - concerning the equation, it is the eqation used to calculate a sine wave:

    http://en.wikipedia.org/wiki/Sine_wave


    So then were you able to control the starting value ok???

    I was thinking it might be a little more complicated than I initially thought.

    It seems like you would need to calculate a valid value differential from the time, because in fact there are an infinite number of possible values that would result in the wave coinciding with your desired output.

    Also it seems like this value would change depending on the width (periode) of the wave...

    Not sure but I definitely want to crack it.

    ReplyDelete
  19. In my sketch i use a webserver for user to change values in a led. When the LED is 255 and user change it to 100 or 200 , i would like to implement a smooth passage from one value to another. But without using the delay!...

    basically i need a function that fade from given value1 to given value2 with an Artificial 1 second delay

    ReplyDelete
  20. Why do you introduce the variables displace and displace2?
    For technical or aesthetic reasons?

    ReplyDelete
  21. sorry its been quite awhile since i did this project. If memory serves me correctly the displace values simply serve to start the diff colors at different times so they dont fluctuate at the same time. I think there was a control to change these displace times.

    ReplyDelete
    Replies
    1. so the initial color starts at 0 - the second uses displace and the third displace2

      Delete
  22. I've been a raw foodist since January 2000, and have written five books around the subject. I also present a TV show all about raw food. You could say I'm passionate about it. But WHY? What can raw food do for you that cooked food can't? Well, how about this... Cow Ghee

    ReplyDelete
  23. I've been a raw foodist since January 2000, and have written five books around the subject. I also present a TV show all about raw food. You could say I'm passionate about it. But WHY? What can raw food do for you that cooked food can't? Well, how about this... authentic italian recipes

    ReplyDelete
  24. I've been a raw foodist since January 2000, and have written five books around the subject. I also present a TV show all about raw food. You could say I'm passionate about it. But WHY? What can raw food do for you that cooked food can't? Well, how about this... bán chanh dây

    ReplyDelete
  25. I've been a raw foodist since January 2000, and have written five books around the subject. I also present a TV show all about raw food. You could say I'm passionate about it. But WHY? What can raw food do for you that cooked food can't? Well, how about this... giống chanh không hạt

    ReplyDelete
  26. Starting your baby on solids might seem challenging for moms, especially in large families. Here's good news: it does not have to be! Moms have been doing it for centuries without baby food in jars or other special baby items. This traditional and natural approach will not only save you money and effort, it will also guarantee and soft and healthy introduction of solid food into your baby's diet. Cultured ghee benefits

    ReplyDelete
  27. I've been a raw foodist since January 2000, and have written five books around the subject. I also present a TV show all about raw food. You could say I'm passionate about it. But WHY? What can raw food do for you that cooked food can't? Well, how about this... best cast iron skillet for outdoors

    ReplyDelete
  28. The most ideal way to store vegetables and fruits at home, grocery stores or restaurants is to store them on a vegetable rack. It is a smart way to store vegetables in your kitchen; it gives you easy access to vegetables while cooking. Vegetables are the most natural from of food with each one of them being rich in one type of vitamin or the other. carote

    ReplyDelete
  29. This comment has been removed by the author.

    ReplyDelete
  30. Low maintenance gardens need not look like the bare type, not many plants, lots of rocks, paving, gravel etc. You can have a beautiful garden following a few principles. Choice of plants, type of plants, garden layout, mulching and a well thought out garden design. gilbert tree trimming

    ReplyDelete
  31. I've been a raw foodist since January 2000, and have written five books around the subject. I also present a TV show all about raw food. You could say I'm passionate about it. But WHY? What can raw food do for you that cooked food can't? Well, how about this... goodies online

    ReplyDelete
  32. I've been a raw foodist since January 2000, and have written five books around the subject. I also present a TV show all about raw food. You could say I'm passionate about it. But WHY? What can raw food do for you that cooked food can't? Well, how about this... https://golden-corral-complete-details-ultimate-guide-91.webselfsite.net/

    ReplyDelete
  33. I've been a raw foodist since January 2000, and have written five books around the subject. I also present a TV show all about raw food. You could say I'm passionate about it. But WHY? What can raw food do for you that cooked food can't? Well, how about this... cheap pineapple tart

    ReplyDelete
  34. Are you prepared for a food crisis? The western world takes for granted the food that we eat daily. We assume that since we have always had easy access to it, we always will. The truth is that our food supply is in a critical state and will eventually fail. Plan and prepare a food reserve to help you and your loved ones survive a food shortage or other disaster A2 Cow Ghee

    ReplyDelete
  35. Do you find yourself 'emotionally eating' most of the time? When was the last time you were at your goal weight/shape? Have you ever wondered how to overcome food cravings WITHOUT any willpower? Finally, a scientific approach has been proven. Food cravings are REAL but overcoming them can be EASY. You know you will want to read this because we want to share it with you. 토토먹튀사이트검증

    ReplyDelete
  36. You have done a great job on this article. It’s very readable and highly intelligent. You have even managed to make it understandable and easy to read. You have some real writing talent. Thank you.출장안마


    ReplyDelete
  37. Low maintenance gardens need not look like the bare type, not many plants, lots of rocks, paving, gravel etc. You can have a beautiful garden following a few principles. Choice of plants, type of plants, garden layout, mulching and a well thought out garden design. Tree Removal Kelowna

    ReplyDelete
  38. Take a moment to imagine your garden. The birds are chirping, insects buzzing, the wind moving gently and gracefully through the plants and trees. What is missing in that picture? You sitting with the doors and windows open from your gorgeous garden cabin with a cool glass of iced tea, a book and a sense of utter contentment, of course! Garden cabins are the hottest home improvement and garden trend these days. Trust me, you need one. Tree Maintenance

    ReplyDelete