Pages: 1 [2] 3 4 5   Go Down
Author Topic: LEDS and 74HC595 shift register  (Read 7904 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
God Member
*****
Karma: 0
Posts: 731
skcor oniudrA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Here's a sample php script that toggles the value of LED7 in the left (1st) group of 8 LED's

Code:
<?php

// php demo script - toggle LED 7 on and off via www

// runs on yourwww.com - save it as toggle.php

// All LEDs on = FF FF
// LED 7 off = FF BF
// LED 7 on = FF FF

// Read the current values

$file fopen("settings.txt","r"); // open settings.txt
$settings=fread($file,filesize("settings.txt")); // read whole file

// split pairs into strings
$myhex1 substr($settings02);
$myhex3 substr($settings32);

// show user what the present values are
echo $myhex1;
echo 
"<br />";
echo 
$myhex3;
echo 
"<br />";

fclose($file);

// toggle LED 7


// if LED 7 is on - turn it off
if ($myhex3=="FF") {
      
$newhex3="BF";
}

// if LED 7 is off - turn it on
if ($myhex3=="BF") {
      
$newhex3="FF";
}

$towrite=$myhex1// restore right settings 
$towrite .=chr(9); // add in tab delimiter used by processing
$towrite .=$newhex3// store new value for left settings - LED 7

// show the user what the new values are
echo "<br />";
echo 
$towrite;

// write values to settings.txt
$file fopen("settings.txt","w");
fwrite($file,"$towrite\n");
$file fclose($file);

?>


To toggle the LED status, press the refresh button on the browser. Response time's really good, about 1 second - so I could be sitting in France turning a LED on and off in the US via www.myserver.com/toggle.php

I know there's ways of doing this using other hardware and software. But I haven't seen anything Arduino like that I could recreate, plus, I like being able to add an indefinite number of 595's so the number of LED's, or relay's, one can control, is unlimited.

595's are cheap too - 4 bucks for 10 off eBay if you don't mind the dodgy sellers and their "floor sweeps".
« Last Edit: August 16, 2007, 04:54:19 am by John_Ryan » Logged

0
Offline Offline
God Member
*****
Karma: 0
Posts: 731
skcor oniudrA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

If you want to have all LEDs off, and only toggle LED 7 on and off, change the hex values from

// All LEDs on = FF FF
// LED 7 off = FF BF
// LED 7 on = FF FF

to

// All LEDs off = 00 00
// LED 7 on = 00 40
// LED 7 off = 00 00

It's actually LED 2, not 7, depending from which direction you look at the LEDs on the board.

I've got all the LEDs fired up to determine how long they'll last, but others may just want turn individual LEDs on and off, while leaving the rest off.

Here's an online binary to hex converter script I stumbled across, it's handy for visualizing if your not a binary to hex guru.

http://www.mathsisfun.com/binary-decimal-hexadecimal-converter.html

Logged

0
Offline Offline
God Member
*****
Karma: 0
Posts: 731
skcor oniudrA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Now I want to add the 4021's - can anyone see a problem with this layout ?



I want the board to multiplex inputs and outputs at the same time, and use a hacked version of the code from both examples, but, before I go destroying the board I thought I'd check with you guru's first smiley
« Last Edit: August 18, 2007, 05:30:22 am by John_Ryan » Logged

0
Offline Offline
God Member
*****
Karma: 0
Posts: 731
skcor oniudrA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I'd been sifting through some of Tom's code and came across a processing/php solution to sending values from Arduino, via processing, to php, and then php writing the values to a .txt file on a web server.

This is significant for me, because I have a means of sending values from a .txt file stored on a web server to Arduino via processing, and now I have a way of Arduino talking back to the server via processing via php.

For example, I might like to have a digital input for each LED, so if a switch is turned on or off, the server side script can decide if it's going to turn a LED on or off.

code for processing

Code:

/* http client
 original code by Tom Igoe
 Starts a network client that connects to a server on port 80,
 sends an HTTP 1.1 GET request.
 Sends an input value to php
 php writes the value to a text file on a web server
  */
import processing.net.*;
Client client;
int inputValue = 33;                    // number representing the input switch - this would be determined by the value Arduino sends to processing

void setup()
{
  // open a TCP socket to the host:
  client = new Client(this, "yourserver.com", 80);
  // send the HTTP GET request:
  client.write("GET /~youraccount/switch.php?receivedValue=" + inputValue + " HTTP/1.1\n");
  client.write("HOST: yourserver.com\n\n");
   println("\n\nSend complete\n");
}


php - switch.php

Code:
<?php
$filename 
'switched.txt';
// create string from the POST
  
$dataString $dataString.$_REQUEST['receivedValue'];
  
// add a linefeed and carriage return
  
$dataString $dataString."\r\n";

//  make sure the file exists and is writable first:
if (is_writable($filename)) {

   
// Open $filename in write mode
   
if (!$handle fopen($filename'w')) {
         echo 
"Can't open file $filename";
         exit;
   }

   
// Write $dataString to the opened file.
   
if (fwrite($handle$dataString) == FALSE) {
       echo 
"Can't write to file $filename";
       exit;
   }
  
// data successfully written:
   
echo "Wrote $dataString to file $filename";
   
// send a 0 to tell the remote side we're done:
  
echo "\0";
  
// close the file
   
fclose($handle);

} else {
   echo 
"The file $filename is not write-enabled.";
}
end;
?>



So, depending on what processing receives from Arduino, the value gets stored in the file switched.txt, located on the web server.

Once I've integrated the 4021's - I'll provide a more thorough explanation of how the Arduino sends and receives data from a web based php script, via processing.

I can think of a number of reasons why you would want web based communications like this, home security springs to mind, remote sensor monitoring via a web based interface, and being able to send data to Arduino over the internet.  
« Last Edit: August 20, 2007, 07:14:22 pm by John_Ryan » Logged

0
Offline Offline
God Member
*****
Karma: 0
Posts: 731
skcor oniudrA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

So, the layout (above) with both 595 and 4021's works fine - nothing blew up, I'll post pic's when I get time.

The 595's still respond, so the LED switching works perfectly. And the serial window in Arduino echo's which input switch is on when a button is pushed.

In processing, I used port.read(); to collect the value of the switch sent by Arduino.

So now I'm writing a method to debounce the switch, once that's done - I'll incorporate the method of writing the input value from processing, to a .txt file on the server.

Then, I'll publish the php code for reading the values of switches, and changing which LED's are displayed, via a web based interface, without using Xport or any other device for handling web based communications with Arduino, just, USB Arduino and an internet connection, processing, php, 2 x .txt files, and a trusty Linux server smiley



Logged

0
Offline Offline
God Member
*****
Karma: 0
Posts: 731
skcor oniudrA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Here's the Arduino code for both 595's and 7021's

I've currently got 3 x 595's and 1 x 7021 wired up, and all works well.

On the processing side it's a bit more of a headache. The methods for getting information from a .txt file stored on a web server are prone to falling over.

One of the methods falls over if there's a small interruption between my laptop and the router, which happens if 'something in the air' causes a temporary break in the connection. That method comes with a small note that there is "no error trapping", so I'm really bummed out about that.

The second method works better at handling breaks in the connection, but does a giant wobbly when there's a serialEvent anytime processing is sent the value of an input switch. The "Exception" error it throws is a hot little topic over at processing, and is the subject of a great deal of puzzlement, so I'm also bummed out about that. So I posed the question of how "in Beta" is processing, I also don't expect to get that answered.

I've posted over at processing, but I'm not confident of a solution anytime. Perhaps it's worth mentioning to people that processing has lots of bugs that aren't being fixed, and judging by their Alexa ranking, traffic at the site appears to be diminishing.

Code:

//  ::: Code for using 74HC595's for controlling arrays of LED's, and 7021's for reading arrays of switches

int latchPin = 8;  //Pin connected to ST_CP of 74HC595
int clockPin = 12; //Pin connected to SH_CP of 74HC595
int dataPin = 11; //Pin connected to DS of 74HC595

int inplatchPin = 10; //Pin connected to ST_CP of 7021
int inpdataPin = 9; //Pin connected to SH_CP of 7021
int inpclockPin = 7; //Pin connected to DS of 7021

int sentSwitch = 2;
//Define variables to hold the data
//for shift register.
//starting with a non-zero numbers can help
//troubleshoot
byte switchVar1 = 72;  //01001000
byte lowcBitNum = 7;
byte dBitNum = 6;
byte eBitNum = 5;
byte fBitNum = 4;
byte gBitNum = 3;
byte aBitNum = 2;
byte bBitNum = 1;
byte highcNum = 0;
boolean dBit;
long time = 0;
long debounce = 8000; // big long debounce
 
int firstByte;
int secondByte;
int thirdByte;
int val;
int serialInArray[4];
int serialCount = 0;
 
void setup() {
      //set pins to output because they are addressed in the main loop
      pinMode(latchPin, OUTPUT);
      //define pin modes
      pinMode(inplatchPin, OUTPUT);
      pinMode(inpclockPin, OUTPUT);
      pinMode(inpdataPin, INPUT);
      Serial.begin(9600);
}

void loop() {
      digitalWrite(inplatchPin,1);  //Pulse the latch pin / collect parallel data
      delayMicroseconds(20);
      digitalWrite(inplatchPin,0); //set it to 0 to transmit data serially

      //collect each shift register into a byte

      if (millis() - time > debounce) {

            switchVar1 = shiftIn(inpdataPin, inpclockPin);

            if (getBit(switchVar1, highcNum)) {
                  Serial.println("0");
                  time = millis();
            }
            if (getBit(switchVar1, bBitNum)) {
                  Serial.println("1");
                  time = millis();
            }
            if (getBit(switchVar1, aBitNum)) {
                  Serial.println("2");
                  time = millis();
            }
            if (getBit(switchVar1, gBitNum)) {
                  Serial.println("3");
                  time = millis();
            }
            if (getBit(switchVar1, fBitNum)) {
                  Serial.println("4");
                  time = millis();
            }
            if (getBit(switchVar1, eBitNum)) {
                  Serial.println("5");
                  time = millis();
            }
            if (getBit(switchVar1, dBitNum)) {
                  Serial.println("6");
                  time = millis();
            }
            if (getBit(switchVar1, lowcBitNum)) {
                  Serial.println("7");
                  time = millis();
            }
  
      }

  if (Serial.available() > 0) {
      serialInArray[serialCount] = Serial.read();
      serialCount++;
      if (serialCount > 3 ) {
            firstByte = serialInArray[1];
            secondByte = serialInArray[2];
            thirdByte = serialInArray[3];
            //ground latchPin and hold low during transmit
            digitalWrite(latchPin, 0);
            shiftOut(dataPin, clockPin, firstByte);
            shiftOut(dataPin, clockPin, secondByte);
            shiftOut(dataPin, clockPin, thirdByte);      
            //return the latch pin high to signal chip that it no longer needs to listen for information
            digitalWrite(latchPin, 1);
            serialCount = 0;
            sentSwitch = 2;
            }
      }
}

////// ----------------------------------------shiftOut function

void shiftOut(int myDataPin, int myClockPin, byte myDataOut) {
  int i=0;
  int pinState;
  pinMode(myClockPin, OUTPUT);
  pinMode(myDataPin, OUTPUT);
  digitalWrite(myDataPin, 0);
  digitalWrite(myClockPin, 0);
  for (i=7; i>=0; i--)  {
    digitalWrite(myClockPin, 0);
    if ( myDataOut & (1<<i) ) {
      pinState= 1;
    }
    else {
      pinState= 0;
    }
    digitalWrite(myDataPin, pinState);
    digitalWrite(myClockPin, 1);
    digitalWrite(myDataPin, 0);
  }
  digitalWrite(myClockPin, 0);
}
 
 
////// ----------------------------------------shiftIn function

byte shiftIn(int myinpDataPin, int myinpClockPin) {
  int inpi;
  int inptemp = 0;
  int inppinState;
  byte myinpDataIn = 0;
  pinMode(myinpClockPin, OUTPUT);
  pinMode(myinpDataPin, INPUT);

//Count Down
  for (inpi=7; inpi>=0; inpi--)
  {
    digitalWrite(myinpClockPin, 0);
    delayMicroseconds(0.2);
    inptemp = digitalRead(myinpDataPin);
    if (inptemp) {
      inppinState = 1;
      myinpDataIn = myinpDataIn | (1 << inpi);
    }
    else {
      inppinState = 0;
    }
    digitalWrite(myinpClockPin, 1);
  }
  return myinpDataIn;
}

////// ----------------------------------------getBit
boolean getBit(byte myVarIn, byte whatBit) {
  boolean bitState;
  bitState = myVarIn & (1 << whatBit);
  return bitState;
}
Logged

0
Offline Offline
God Member
*****
Karma: 0
Posts: 731
skcor oniudrA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

My next attempt will be to wire up a parallax RFID board I've got, and integrate code from this example.

http://www.arduino.cc/playground/Learning/PRFID

The only problem I can see, is that it uses a 2400 baud serial connection, whereas, I'm already set up at 9600

Code:
// RFID reader for Arduino
// Wiring version by BARRAGAN <http://people.interaction-ivrea.it/h.barragan>
// Modified for Arudino by djmatic


int  val = 0;
char code[10];
int bytesread = 0;

void setup() {

Serial.begin(2400); // RFID reader SOUT pin connected to Serial RX pin at 2400bps
pinMode(2,OUTPUT);   // Set digital pin 2 as OUTPUT to connect it to the RFID /ENABLE pin
}

 digitalWrite(2, LOW);                  // Activate the RFID reader


 void loop() {

  if(Serial.available() > 0) {          // if data available from reader
    if((val = Serial.read()) == 10) {   // check for header
      bytesread = 0;
      while(bytesread<10) {              // read 10 digit code
        if( Serial.available() > 0) {
          val = Serial.read();
          if((val == 10)||(val == 13)) { // if header or stop bytes before the 10 digit reading
            break;                       // stop reading
          }
          code[bytesread] = val;         // add the digit          
          bytesread++;                   // ready to read next digit  
        }
      }
      if(bytesread == 10) {              // if 10 digit read is complete
        Serial.print("TAG code is: ");   // possibly a good TAG
        Serial.println(code);            // print the TAG code
      }
      bytesread = 0;
           delay(500);                       // wait for a second
    }
  }
}

// extra stuff
// digitalWrite(2, HIGH);             // deactivate RFID reader


Does anyone know how I might toggle between 9600, and 2400 for the PRFID ?

 
Logged

Daniel
Guest
 Bigger Bigger  Smaller Smaller  Reset Reset

hey

do you mean running two peripherals on the same pin, at different baud rates? Sounds like trouble... some characters sent slow may look like valid data to the fast connection, and vice-versa. You might have to add a gate or something like a CD4066 to selectively connect and disconnect the serial form each device.

D
Logged

0
Offline Offline
God Member
*****
Karma: 0
Posts: 731
skcor oniudrA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

yeah, unfortunately that's exactly what I meant.

So there's no way to toggle the baud rates on the Arduino?

Or, can the PRFID work at 9600 baud? I couldn't find a spec sheet. The reason I need it to work on the Arduino, is the troublesome out-of-Arduino processing, inputs are collected by the Arduino, then sent to processing, which posts them via php to a .txt file. The php handles the RFID, not using the PRFID, once it's got a scan it tells processing via the .txt file to tell Arduino to turn a LED on or off.

So, the states of the LEDs can be controlled either by inputs, or server side events. Part of that requires the inputs get paired with the RFID number that's scanned.

If I could get the PRFID working inside Arduino, it'll help trim back on the processing side, which has a few bugs that aren't likely to get resolved anytime soon.

Logged

0
Offline Offline
God Member
*****
Karma: 0
Posts: 731
skcor oniudrA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Ah, ok, so I found the spec sheet. It communicates at 2400 baud and can't be changed.

 :'(
Logged

0
Offline Offline
God Member
*****
Karma: 0
Posts: 731
skcor oniudrA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

So, I've changed the baud rate to 2400, and cut the value of the debounce in half, and it works fine.

Now that's established, I can operate the PRFID at 2400 baud, this should make for an interesting test smiley

Logged

0
Offline Offline
God Member
*****
Karma: 0
Posts: 731
skcor oniudrA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Here's the PRFID code integrated. When one of the switches is turned on, a reading from the RFID scanner is taken.


Code:
int latchPin = 8;  //Pin connected to ST_CP of 74HC595
int clockPin = 12; //Pin connected to SH_CP of 74HC595
int dataPin = 11; //Pin connected to DS of 74HC595

int inplatchPin = 10; //Pin connected to ST_CP of 7021
int inpdataPin = 9; //Pin connected to SH_CP of 7021
int inpclockPin = 7; //Pin connected to DS of 7021

int rfidPin = 2; //Pin connected to PRFID

char code[10];
int bytesread = 0;
int checkRFID=0;

int sentSwitch = 2;
//Define variables to hold the data
//for shift register.
//starting with a non-zero numbers can help
//troubleshoot
byte switchVar1 = 72;  //01001000
byte lowcBitNum = 7;
byte dBitNum = 6;
byte eBitNum = 5;
byte fBitNum = 4;
byte gBitNum = 3;
byte aBitNum = 2;
byte bBitNum = 1;
byte highcNum = 0;
boolean dBit;
long time = 0;
long debounce = 8000; // big long debounce
 
int firstByte;
int secondByte;
int thirdByte;
int val;
int serialInArray[4];
int serialCount = 0;
 
void setup() {
      //set pins to output because they are addressed in the main loop
      pinMode(latchPin, OUTPUT);
      //define pin modes
      pinMode(inplatchPin, OUTPUT);
      pinMode(inpclockPin, OUTPUT);
      pinMode(inpdataPin, INPUT);
      pinMode(rfidPin,OUTPUT);   // Set digital pin 2 as OUTPUT to connect it to the RFID /ENABLE pin
      Serial.begin(9600);
}

void loop() {
      digitalWrite(inplatchPin,1);  //Pulse the latch pin / collect parallel data
      delayMicroseconds(20);
      digitalWrite(inplatchPin,0); //set it to 0 to transmit data serially

      //collect each shift register into a byte

      if (millis() - time > debounce) {

            switchVar1 = shiftIn(inpdataPin, inpclockPin);

            if (getBit(switchVar1, highcNum)) {
                  Serial.println("0");
                  time = millis();
                  checkRFID=1;
            }
            if (getBit(switchVar1, bBitNum)) {
                  Serial.println("1");
                  time = millis();
                  checkRFID=1;
            }
            if (getBit(switchVar1, aBitNum)) {
                  Serial.println("2");
                  time = millis();
                  checkRFID=1;
            }
            if (getBit(switchVar1, gBitNum)) {
                  Serial.println("3");
                  time = millis();
                  checkRFID=1;
            }
            if (getBit(switchVar1, fBitNum)) {
                  Serial.println("4");
                  time = millis();
                  checkRFID=1;
            }
            if (getBit(switchVar1, eBitNum)) {
                  Serial.println("5");
                  time = millis();
                  checkRFID=1;
            }
            if (getBit(switchVar1, dBitNum)) {
                  Serial.println("6");
                  time = millis();
                  checkRFID=1;
            }
            if (getBit(switchVar1, lowcBitNum)) {
                  Serial.println("7");
                  time = millis();
                  checkRFID=1;
            }
  
      }

  if (Serial.available() > 0) {
      serialInArray[serialCount] = Serial.read();
      serialCount++;
      if (serialCount > 3 ) {
            firstByte = serialInArray[1];
            secondByte = serialInArray[2];
            thirdByte = serialInArray[3];
            digitalWrite(latchPin, 0);      // ground latchPin and hold low during transmit
            shiftOut(dataPin, clockPin, firstByte);
            shiftOut(dataPin, clockPin, secondByte);
            shiftOut(dataPin, clockPin, thirdByte);      
            digitalWrite(latchPin, 1);      // return the latch pin high to signal chip that it no longer needs to listen for information
            serialCount = 0;
            sentSwitch = 2;

            // finished with LED's - now see if we're reading from PRFID

            if (checkRFID > 0)      {                         // if not zero then yes
                  digitalWrite(rfidPin, LOW);                  // Activate the RFID reader
                  if(Serial.available() > 0)      {            // if data available from reader
                        if((val = Serial.read()) == 10)      {      // check for header
                              bytesread = 0;
                              while(bytesread<10)      {      // read 10 digit code
                                    if( Serial.available() > 0)      {
                                          val = Serial.read();
                                          if((val == 10)||(val == 13))      {      // if header or stop bytes before the 10 digit reading
                                          break;                  // stop reading
                                                      }
                                    code[bytesread] = val;      // add the digit            
                                    bytesread++;            // ready to read next digit  
                                                }
                                          }
                                    if(bytesread == 10)      {      // if 10 digit read is complete
                                    Serial.print("TAG code is: ");      // possibly a good TAG
                                    Serial.println(code);            // print the TAG code
                                          }
                                    bytesread = 0;
                                    }
                              }
                        digitalWrite(rfidPin, HIGH);      // deactivate RFID reader
                        checkRFID=0;            // return state to zero
                  } // Finished with reader

            }
      }
}
////// ----------------------------------------shiftOut function
void shiftOut(int myDataPin, int myClockPin, byte myDataOut) {
  int i=0;
  int pinState;
  pinMode(myClockPin, OUTPUT);
  pinMode(myDataPin, OUTPUT);
  digitalWrite(myDataPin, 0);
  digitalWrite(myClockPin, 0);
  for (i=7; i>=0; i--)  {
    digitalWrite(myClockPin, 0);
    if ( myDataOut & (1<<i) ) {
      pinState= 1;
    }
    else {
      pinState= 0;
    }
    digitalWrite(myDataPin, pinState);
    digitalWrite(myClockPin, 1);
    digitalWrite(myDataPin, 0);
  }
  digitalWrite(myClockPin, 0);
}
////// ----------------------------------------shiftIn function
byte shiftIn(int myinpDataPin, int myinpClockPin) {
  int inpi;
  int inptemp = 0;
  int inppinState;
  byte myinpDataIn = 0;
  pinMode(myinpClockPin, OUTPUT);
  pinMode(myinpDataPin, INPUT);
  for (inpi=7; inpi>=0; inpi--)
  {
    digitalWrite(myinpClockPin, 0);
    delayMicroseconds(0.2);
    inptemp = digitalRead(myinpDataPin);
    if (inptemp) {
      inppinState = 1;
      myinpDataIn = myinpDataIn | (1 << inpi);
    }
    else {
      inppinState = 0;
    }
    digitalWrite(myinpClockPin, 1);
  }
  return myinpDataIn;
}
////// ----------------------------------------getBit
boolean getBit(byte myVarIn, byte whatBit) {
  boolean bitState;
  bitState = myVarIn & (1 << whatBit);
  return bitState;
}
Logged

0
Offline Offline
God Member
*****
Karma: 0
Posts: 731
skcor oniudrA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Small problem, the board I've got is a Phidgets USB RFID reader, and it doesn't have a helpful spec sheet.

Are there any other 4100-4102 125khZ 1.6mH options besides the Parallax? The board price is ok, but their shipping price is an absolute rip-off. Many of their distributors sites also don't work, or don't take online sales.

Ideas?

« Last Edit: August 27, 2007, 12:28:44 pm by John_Ryan » Logged

0
Offline Offline
God Member
*****
Karma: 0
Posts: 731
skcor oniudrA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

So I've found a Parallax distributor and ordered a reader, I should have a chance to test the code in a couple of days. In the meantime, I'll start work on the "595 LED micro-boards", I'll post pic's once soldered up and working.
Logged

0
Offline Offline
God Member
*****
Karma: 0
Posts: 731
skcor oniudrA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Parallax arrived. At the moment it's looking like I can't have my RFID and eat my LED's to.

But, the reader and RFID code works, and inputs too, so that's a good start  smiley
Logged

Pages: 1 [2] 3 4 5   Go Up
Jump to: