Code the Drawer Actuator

On the receiving side, our RFM69 Feather M0 and Motor FeatherWing combo has two primary functions: listen for commands transmitted by the chess board system, and then driving the linear actuator's motor either forward or in reverse to open or close the drawer.

Here's the code you'll use below. Again, to use it, make a new document in the Arduino IDE, copy and paste the code, save it as chessBoardPuzzleLockRX.ino and then upload it to the Feather M0.

Download: file
//Chess Board Puzzle Lock RX
// by John Park
// Using Motor FeatherWing on Feather M0 RFM69HCW
// to drive linear actuator based on remote commands


/*************************** Libraries ******************************/
#include <RH_RF69.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <RHReliableDatagram.h>
#include <Wire.h>
#include <Adafruit_MotorShield.h>

/*************************** Motor Defines ******************************/
// Create the motor shield object with the default I2C address
Adafruit_MotorShield AFMS = Adafruit_MotorShield();
// Select which 'port' M1, M2, M3 or M4. In this case, M1
Adafruit_DCMotor *myMotor = AFMS.getMotor(1);

/*************************** Radio Defines ******************************/
// Change to 434.0 or other frequency, must match RX's freq!
#define RF69_FREQ 915.0
#define RFM69_CS      8
#define RFM69_INT     3
#define RFM69_RST     4

// Where to send packets to!
#define DEST_ADDRESS   2
// who am i? (server address)
#define MY_ADDRESS     1

// Singleton instance of the radio driver
RH_RF69 rf69(RFM69_CS, RFM69_INT);

// Class to manage message delivery and receipt, using the driver declared above
RHReliableDatagram rf69_manager(rf69, MY_ADDRESS);

int16_t packetnum = 0;  // packet counter, we increment per xmission


#define LED_PIN 13 //use as a power ON indicator
int drawerClosed = 1; //to keep track of state

/*************************** Setup ******************************/

void setup() {
	Serial.begin(115200);
    //while (!Serial) { delay(1); } // wait for serial console to open. remove if not tethered to computer
	pinMode(LED_PIN, OUTPUT);
	digitalWrite(LED_PIN,HIGH);

	//AFMS.begin();  // create with the default frequency 1.6KHz
	AFMS.begin(1000);  // OR with a different frequency, say 1KHz
	// Set the speed to start, from 0 (off) to 255 (max speed)
	myMotor->setSpeed(255);
	// turn on motor
	//open and close once on startup
	myMotor->run(FORWARD);
	delay(6000);
	myMotor->run(BACKWARD);
	delay(6000);
	myMotor->run(RELEASE);

	/////////////////////// radio ///////////////////////
	
	pinMode(RFM69_RST, OUTPUT);
	digitalWrite(RFM69_RST, LOW);

	Serial.println("Feather RFM69 RX Test!");
	Serial.println();

	// manual reset
	digitalWrite(RFM69_RST, HIGH);
	delay(10);
	digitalWrite(RFM69_RST, LOW);
	delay(10);

	if (!rf69_manager.init()) {
	Serial.println("RFM69 radio init failed");
	while (1);
	}
	Serial.println("RFM69 radio init OK!");

	// Defaults after init are 434.0MHz, modulation GFSK_Rb250Fd250, +13dbM (for low power module)
	if (!rf69.setFrequency(RF69_FREQ)) {
	Serial.println("setFrequency failed");
	}

	// If you are using a high power RF69 eg RFM69HW, you *must* set a Tx power with the
	// ishighpowermodule flag set like this:
	rf69.setTxPower(20, true);  // range from 14-20 for power, 2nd arg must be true for 69HCW

	// The encryption key has to be the same as the one in the server
	uint8_t key[] = { 0x05, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
	                0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
	
	rf69.setEncryptionKey(key);

  	Serial.print("RFM69 radio @");  
  	Serial.print((int)RF69_FREQ);  
  	Serial.println(" MHz");

}


// Dont put this on the stack:
uint8_t data[] = "Message recieved, thanks";
uint8_t buf[RH_RF69_MAX_MESSAGE_LEN];


/*************************** Loop ******************************/

void loop(void) {
	  uint8_t i;//for motor speed
	if (rf69_manager.available()) {

		// Wait for a message addressed to us from the client
		uint8_t len = sizeof(buf);
		uint8_t from;
		if (rf69_manager.recvfromAck(buf, &len, &from)) {
			
			buf[len] = 0; // zero out remaining string
		  
			Serial.print("\nGot packet from #"); Serial.print(from);
			Serial.print(" [RSSI :");
			Serial.print(rf69.lastRssi());
			Serial.print("] : ");
			Serial.println((char*)buf);

			char radiopacket[2];

			if ( buf[0]=='A'){ //this is the unlock code
				Serial.println("Unlocked");

				//turn the motor forward
				///////////////
				if(drawerClosed==1){
					Serial.println("Opening drawer");
					  myMotor->run(FORWARD);
					  //ramp up the speed
					  for (i=0; i<255; i++) {
					    myMotor->setSpeed(i);  
					    delay(10);
					}
					  //how long to run at top speed
					delay(2500); //this will vary depending on the throw of your actuator
					//ramp down the speed
					for (i=255; i!=0; i--) {
					    myMotor->setSpeed(i);  
					    delay(10);
					}
					myMotor->run(RELEASE);
					drawerClosed=0;//prevents multiple open signals from being sent
				}


				// Send a reply back to the originator client
				if (!rf69_manager.sendtoWait(data, sizeof(data), from))
	    			Serial.println("Sending failed (no ack)");
			}

			else if ( buf[0]=='B'){//this is the lock code
				Serial.println("Locked");

				//turn the motor backwards
				///////////////
				if(drawerClosed==0){
					Serial.println("Closing drawer");
					 myMotor->run(BACKWARD);
					 //ramp up the speed
					 for (i=0; i<255; i++) {
					    myMotor->setSpeed(i);  
					    delay(10);
					 }
					//how long to run at top speed
					delay(2500); //this will vary depending on the throw of your actuator
					//ramp down the speed
					for (i=255; i!=0; i--) {
						myMotor->setSpeed(i);  
					    delay(10);
					}
					myMotor->run(RELEASE);
					drawerClosed=1;//prevents multiple open signals from being sent
				}

				// Send a reply back to the originator client
				if (!rf69_manager.sendtoWait(data, sizeof(data), from))
	    		Serial.println("Sending failed (no ack)");
			}	
		}
	}// if radio available
	delay(100); //prevent a flood
}//end loop()

You can see in the code that the first thing it does upon power up or reset is to open and close the drawer once. This resets things to the ready position for the puzzle.

Then, rather than worry about the particular combinations of pieces on the chess board, it only receives two different commands. A letter 'A' means open the drawer, and a letter 'B' means close it! 

You could add other behaviors if you like, including driving different motors, lights, sounds, and so on.

The communication between the two RFM69 Feather boards is encrypted and sent to specific channels, so there's very little chance of an errant message flying through the airwaves and spoiling the fun!
This guide was first published on Aug 25, 2017. It was last updated on Aug 25, 2017. This page (Code the Drawer Actuator) was last updated on Aug 23, 2017.