Go Down

Topic: Trigger a pwm output when motion detected (moderately disappointed -_-) (Read 190 times) previous topic - next topic


Hi, Im a relative newb especially when it comes to code, but i have an idea of what it does.. Heres what I want to do/ what im working on:

I have a motion sensor (PWM3360) and an arduino Uno. I want to take the X and Y data coming from the spi port &/or serial port and output (each seperately) as a pwm output (to 'simulate analog volt')

Is this even possible or am I dreaming?

I have tried adding to the void loop, and telling it to analogWrite. And it turns on when detected but doesnt turn back off when there is no motion. Ive commented on the parts of the code ive already modified. Im thinking im going to have to put in some kind of interrupt at the least....

Ive also attached the datasheet for the pwm3360 and the original source code from John Kicklighter (thanks John). the only difference in the original code and this code is the polling and pin settings.

Apologies in advance, this is quite an undertaking :smiley-confuse: Any ideas would be GREATLY appreciated!

Heres my code

Code: [Select]
* This example bypasses the hardware motion interrupt pin
* and polls the motion data registers at a fixed interval. The result is displayed on the serial port.

#include <SPI.h>
#include <avr/pgmspace.h>

// Registers
#define Product_ID  0x00
#define Revision_ID 0x01
#define Motion  0x02
#define Delta_X_L 0x03
#define Delta_X_H 0x04
#define Delta_Y_L 0x05
#define Delta_Y_H 0x06
#define SQUAL 0x07
#define Raw_Data_Sum  0x08
#define Maximum_Raw_data  0x09
#define Minimum_Raw_data  0x0A
#define Shutter_Lower 0x0B
#define Shutter_Upper 0x0C
#define Control 0x0D
#define Config1 0x0F
#define Config2 0x10
#define Angle_Tune  0x11
#define Frame_Capture 0x12
#define SROM_Enable 0x13
#define Run_Downshift 0x14
#define Rest1_Rate_Lower  0x15
#define Rest1_Rate_Upper  0x16
#define Rest1_Downshift 0x17
#define Rest2_Rate_Lower  0x18
#define Rest2_Rate_Upper  0x19
#define Rest2_Downshift 0x1A
#define Rest3_Rate_Lower  0x1B
#define Rest3_Rate_Upper  0x1C
#define Observation 0x24
#define Data_Out_Lower  0x25
#define Data_Out_Upper  0x26
#define Raw_Data_Dump 0x29
#define SROM_ID 0x2A
#define Min_SQ_Run  0x2B
#define Raw_Data_Threshold  0x2C
#define Config5 0x2F
#define Power_Up_Reset  0x3A
#define Shutdown  0x3B
#define Inverse_Product_ID  0x3F
#define LiftCutoff_Tune3  0x41
#define Angle_Snap  0x42
#define LiftCutoff_Tune1  0x4A
#define Motion_Burst  0x50
#define LiftCutoff_Tune_Timeout 0x58
#define LiftCutoff_Tune_Min_Length  0x5A
#define SROM_Load_Burst 0x62
#define Lift_Config 0x63
#define Raw_Data_Burst  0x64
#define LiftCutoff_Tune2  0x65

//Set this to what pin your "INT0" hardware interrupt feature is on
#define Motion_Interrupt_Pin 9

const int ncs = 10;  //This is the SPI "slave select" pin that the sensor is hooked up to
const int pwm = 3;    //pwm pin out *I modified*
const int pwmY = 5;

int incomingByte = 0;    //for incoming serial data *I modified*

byte initComplete=0;
volatile int xydat[2];
volatile byte movementflag=0;
byte testctr=0;
unsigned long currTime;
unsigned long timer;
unsigned long pollTimer;

//Be sure to add the SROM file into this sketch via "Sketch->Add File"
extern const unsigned short firmware_length;
extern const unsigned char firmware_data[];

void setup() {
 pinMode (ncs, OUTPUT);
 pinMode (pwm, OUTPUT);
 pinMode(Motion_Interrupt_Pin, INPUT);
 digitalWrite(Motion_Interrupt_Pin, HIGH);
 attachInterrupt(9, UpdatePointer, FALLING);




void adns_com_begin(){
 digitalWrite(ncs, LOW);

void adns_com_end(){
 digitalWrite(ncs, HIGH);

byte adns_read_reg(byte reg_addr){
 // send adress of the register, with MSBit = 0 to indicate it's a read
 SPI.transfer(reg_addr & 0x7f );
 delayMicroseconds(100); // tSRAD
 // read data
 byte data = SPI.transfer(0);
 delayMicroseconds(1); // tSCLK-NCS for read operation is 120ns
 delayMicroseconds(19); //  tSRW/tSRR (=20us) minus tSCLK-NCS

 return data;

void adns_write_reg(byte reg_addr, byte data){
 //send adress of the register, with MSBit = 1 to indicate it's a write
 SPI.transfer(reg_addr | 0x80 );
 //sent data
 delayMicroseconds(20); // tSCLK-NCS for write operation
 delayMicroseconds(100); // tSWW/tSWR (=120us) minus tSCLK-NCS. Could be shortened, but is looks like a safe lower bound

void adns_upload_firmware(){
 // send the firmware to the chip, cf p.18 of the datasheet
 Serial.println("Uploading firmware...");

 //Write 0 to Rest_En bit of Config2 register to disable Rest mode.
 adns_write_reg(Config2, 0x20);
 // write 0x1d in SROM_enable reg for initializing
 adns_write_reg(SROM_Enable, 0x1d);
 // wait for more than one frame period
 delay(10); // assume that the frame rate is as low as 100fps... even if it should never be that low
 // write 0x18 to SROM_enable to start SROM download
 adns_write_reg(SROM_Enable, 0x18);
 // write the SROM file (=firmware data)
 SPI.transfer(SROM_Load_Burst | 0x80); // write burst destination adress
 // send all bytes of the firmware
 unsigned char c;
 for(int i = 0; i < firmware_length; i++){
   c = (unsigned char)pgm_read_byte(firmware_data + i);

 //Read the SROM_ID register to verify the ID before any other register reads or writes.

 //Write 0x00 to Config2 register for wired mouse or 0x20 for wireless mouse design.
 adns_write_reg(Config2, 0x00);

 // set initial CPI resolution
 adns_write_reg(Config1, 0x15);

void performStartup(void){
 adns_com_end(); // ensure that the serial port is reset
 adns_com_begin(); // ensure that the serial port is reset
 adns_com_end(); // ensure that the serial port is reset
 adns_write_reg(Power_Up_Reset, 0x5a); // force reset
 delay(50); // wait for it to reboot
 // read registers 0x02 to 0x06 (and discard the data)
 // upload the firmware
 Serial.println("Optical Chip Initialized");

void UpdatePointer(void){

   //write 0x01 to Motion register and read from it to freeze the motion values and make them available
   adns_write_reg(Motion, 0x01);

   xydat[0] = (int)adns_read_reg(Delta_X_L);
   xydat[1] = (int)adns_read_reg(Delta_Y_L);

void dispRegisters(void){
 int oreg[7] = {
   0x00,0x3F,0x2A,0x02  };
 char* oregname[] = {
   "Product_ID","Inverse_Product_ID","SROM_Version","Motion"  };
 byte regres;


 int rctr=0;
 for(rctr=0; rctr<4; rctr++){
   regres = SPI.transfer(0);

int convTwosComp(int b){
 //Convert from 2's complement
 if(b & 0x80){
   b = -1 * ((b ^ 0xff) + 1);
 return b;

void loop() {

 currTime = millis();
 if(currTime > timer){    
   timer = currTime + 2000;
 if(currTime > pollTimer){
   xydat[0] = convTwosComp(xydat[0]);
   xydat[1] = convTwosComp(xydat[1]);
     if(xydat[0] != 0 || xydat[1] != 0){
       Serial.print("x = ");
       Serial.print(" | ");
       Serial.print("y = ");
   pollTimer = currTime + 20;

//*unmodified code ended here

    if (xydat[0] > 1){
   incomingByte = Serial.read();
     int val = Serial.read();
    analogWrite(pwm, val ); // do Thing A
else if (xydat[0] == 0);
 // do Thing B



To make it easy for people to help you please modify your post and use the code button </>
Code: [Select]
so your code looks like this and is easy to copy to a text editor. See How to use the Forum

I don't see any link to a datasheet.

Two or three hours spent thinking and reading documentation solves most programming problems.


your code seems over complicated for what you're trying to do. I would suggest breaking it up into several parts.

1. get the data from your sensor and print it to the serial monitor. Reduce all the unneeded code from the example until you have it to the bare minimum that does what you want.

2. make a separate sketch that can output pwm signals (I assume you're putting the PWM signals through a low pass filter to "simulate analog voltage") and verify that does what you want.

3. Figure out what conditions you want the PWM to output.

4. Figure out how to combine 1 & 2 using step 3. It should be pretty straight forward. You may have to map values to correspond with the analogWrite function.


Robin2 sorry about that, just fixed the code, heres a link to the datasheet http://www.pixart.com/upload/PMS0058-PMW3360DM-T2QU-NNDS-R1.30-06042016_20160902201411.pdf

dustin02rsx - Thanks for that, yeah the code is super complicated and it cant be that complicated to do what i want it to do, i just dont know what to keep and what to get rid of... a lot of the setup is spi, would a DAC work in place of PWM? Im very tempted


Code: [Select]

 if(currTime > timer){   
 if(currTime > pollTimer){

These 2 lines will bite you if the sketch runs > 49.7 days.

What works through rollover is unsigned subtraction to give the difference between any two times, as long as the difference can fit in a time variable (32 bit unsigned long can time 49.7_some days).

elapsed_time = end_time - start_time;


if ( millis() - start_time >= wait_interval ) // time's up, do the thing

It takes at least 2 saved values, the start and interval or end to -always- get time right using a single line of code.
1) http://gammon.com.au/blink  <-- tasking Arduino 1-2-3
2) http://gammon.com.au/serial <-- techniques howto
3) http://gammon.com.au/interrupts
Tasking: techniques make the trees, the sketch is the woods.
See the woods and the trees, comment both.


Ok now im cooking with gas! Ive been emailing back and fourth with John and he made a good point.

"Sounds like you really just want to raise a pin from 0v to 5v for a short period of time after motion has been detected.  For that, a simple digitalWrite statement followed by a timer of how long it will be on (a few milliseconds maybe), then another digitalWrite to lower the pin back to 0v again."

So this is what I changed...
Code: [Select]
if(currTime > timer){   
    timer = currTime + 500;
    digitalWrite(3, LOW);
  if(currTime > pollTimer){
    xydat[0] = convTwosComp(xydat[0]);
    xydat[1] = convTwosComp(xydat[1]);
      if(xydat[0] != 0 || xydat[1] != 0){
        Serial.print("x = ");
        Serial.print(" | ");
        Serial.print("y = ");
        digitalWrite(3, HIGH);
    pollTimer = currTime + 10;

Now im just not sure how im going to seperate the X and Y axis'............

Go Up