Okay, I guess this will be the first real post on the project log as such because I am starting it again with the outlined goal in mind. As you read through, I would please like to ask that you take the following under consideration:
1: Because all of my previous projects (including outside of electronics) have always been for personal use & never really shared, I want to make it clear that this is the first time I have ever done any kind of project log. Do not expect it to be easy reading etc as there will be a lot of words/personal thoughts to give an insight into what I want to achieve & how as a complete newbie I am thinking about achieving those goals.
2: I am a complete newbie with coding so please bare that in mind. Most of the code will be non-optimal, butchered together from examples, done in a back to front way etc. I appreciate that many of you will find these things trivial, however for me many of them will be big achievements! Lets just remember we all started somewhere.
3: Finally, I have my goal in my head & many parts of that have already been stated in the previous couple of posts. There is a chance that some of those goals/ideas may get changed or modified along the way to tie in with my skill set.
I appreciate advice & constructive criticism, but lets try to keep the plain negative things to ourselves, after all makers are supposed to support & encourage each other!
The first update
What I thought was a huge achievement for today & what I hope will help with testing during the rest of the project was getting an ESP-NOW remote control to work! Its nothing fancy at all, just a 4x4 button matrix module soldered onto the side of an ESP8266 with onboard 18650 battery, it looks rough at the moment but I will design some kind of enclosure to 3d print over the next few days. I really like that there is an onboard charging feature via micro USB which will certainly make things easier when I am stuck in a tent trying to keep the batteries full! So here we have a photo of its current state & I will discuss the code / thoughts afterwards.
So the following code is kind of a butchered variation of the keypad.h example & an ESP-Now example I found online. Looking at the photo of the matrix it would make sense that (S1, S2, S3, S4) was the top row, but no matter how I declared my pins..... the best I could get was for it to be the right most column, which would make sense if the header was at the top of the matrix. In the end it was easier to follow that assumption & just rename the buttons in the code.
In order for me to send commands I thought I would use a simple 3 digit integer comprised of "Row, Column, Press type". Currently the code only implements one press type but later on as I learn more I would like there to be at least short/long press. My thinking being is that my group of friends all have a sense of dark humour so many of the responses will be tailored around that, however being at festivals etc, I also have to think about the children, so even though all of the buttons will cause the same "type" of responses I am thinking about splitting them into short press = child friendly / long press = not child friendly. Simple example of commands being sent;
Row 2, Column 4, short press = 241
Row 3, Column 1, long press = 312
I think that should make it easy to read/code for each command without having to refer to "which code is that button" sheet etc and also make it fairly easy to decode on the other end as all the commands will be following the same 3 digit system.
So here is the code I am currently running on the controller:
#include <ESP8266WiFi.h>
#include <espnow.h>
#include <Keypad.h>
const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns
//define the symbols on the buttons of the keypads
//Arranged strangely because my keypad is orientated 90 degree to the right, right column = top row
char hexaKeys[ROWS][COLS] = {
{'C','8','4','0'},
{'D','9','5','1'},
{'E','A','6','2'},
{'F','B','7','3'}
};
byte rowPins[ROWS] = {D4, D3, D2, D1}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {D8, D7, D6, D5}; //connect to the column pinouts of the keypad
//initialize an instance of class NewKeypad
Keypad customKeypad = Keypad( makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);
// REPLACE WITH RECEIVER MAC Address
uint8_t broadcastAddress[] = {0x60, 0x01, 0x94, 0x4A, 0x8D, 0xCB};
// Structure example to send data
// Must match the receiver structure
typedef struct struct_message {
int a;
} struct_message;
// Create a struct_message called myData
struct_message myData;
unsigned long lastTime = 0;
unsigned long timerDelay = 2000; // send readings timer
int LEDpin = 16;
int command = 0; //keypad command to send
// Callback when data is sent
void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) {
Serial.print("Last Packet Send Status: ");
if (sendStatus == 0){
Serial.println("Delivery success");
}
else{
Serial.println("Delivery fail");
}
}
void setup() {
pinMode(LEDpin, OUTPUT);
digitalWrite(LEDpin, LOW);
// Init Serial Monitor
Serial.begin(115200);
delay(1000);
Serial.println("Serial started");
digitalWrite(LEDpin, HIGH);
// Set device as a Wi-Fi Station
WiFi.mode(WIFI_STA);
// Init ESP-NOW
if (esp_now_init() != 0) {
Serial.println("Error initializing ESP-NOW");
return;
}
// Once ESPNow is successfully Init, we will register for Send CB to
// get the status of Trasnmitted packet
esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER);
esp_now_register_send_cb(OnDataSent);
// Register peer
esp_now_add_peer(broadcastAddress, ESP_NOW_ROLE_SLAVE, 1, NULL, 0);
}
void loop() {
digitalWrite(LEDpin, HIGH);
char customKey = customKeypad.getKey();
if (customKey){
if (customKey == '0'){command = 111;}
if (customKey == '1'){command = 121;}
if (customKey == '2'){command = 131;}
if (customKey == '3'){command = 141;}
if (customKey == '4'){command = 211;}
if (customKey == '5'){command = 221;}
if (customKey == '6'){command = 231;}
if (customKey == '7'){command = 241;}
if (customKey == '8'){command = 311;}
if (customKey == '9'){command = 321;}
if (customKey == 'A'){command = 331;}
if (customKey == 'B'){command = 341;}
if (customKey == 'C'){command = 411;}
if (customKey == 'D'){command = 421;}
if (customKey == 'E'){command = 431;}
if (customKey == 'F'){command = 441;}
Serial.println(command);
myData.a = command;
// Send message via ESP-NOW
esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
}
}
As for the receiver in the parrot itself...... I have stripped everything back to just an NodeMCU ESP-12e module for testing communication. The final will be running on some kind of ESP32 module which I have not decided on yet, but wanted to at least get a basic ESP-NOW system working that I can port across later on. Currently all that it does is listen for a command & then print that integer to serial, while also printing the seconds since last receiving a command. Pretty boring for an update I know, but for me this was the first big step to take in this project and what I thought was going to be one of the hardest parts!!! So here is the code I am running on the receiver which is a modified example code that currently does the job!
#include <ESP8266WiFi.h>
#include <espnow.h>
int command = 0; //command recieved from remote
int lastMessage = 0; //seconds since last message
// Structure example to receive data
typedef struct struct_message {
int a;
} struct_message;
// Create a struct_message called myData
struct_message myData;
// Callback function that will be executed when data is received
void OnDataRecv(uint8_t * mac, uint8_t *incomingData, uint8_t len) {
memcpy(&myData, incomingData, sizeof(myData));
Serial.print("Command: ");
Serial.println(myData.a);
Serial.println();
command = int(myData.a);
lastMessage = 0;
}
void setup() {
// Initialize Serial Monitor
Serial.begin(115200);
delay(2500);
Serial.println("Serial Running");
// Set device as a Wi-Fi Station
WiFi.mode(WIFI_STA);
// Init ESP-NOW
if (esp_now_init() != 0) {
Serial.println("Error initializing ESP-NOW");
return;
}
esp_now_set_self_role(ESP_NOW_ROLE_SLAVE);
esp_now_register_recv_cb(OnDataRecv);
}
void loop() {
delay(1000);
lastMessage += 1;
Serial.print("Sec's since last message = ");
Serial.println(lastMessage);
}
Final thoughts for the day
Again, this is probably pretty trivial to many of you, but I am quite proud that I managed to get this working the way it currently is. As this is going to be a mobile animatronic it will not always be possible to connect up to serial for error/debug messages, with that in mind I have added a single WS2812 LED into the chest/speaker area of the parrot which will serve two functions: 1, to add a glowing effect while it is talking, I would like to try & imitate something a little like the warm glow of an old vacuum tube that varies on how much is being said. 2, for functionality I would like to write a series of colours/pulses which would work as a very rudimentary "error code" when out in the field...... No I will not be in a position to plug into a workstation & modify code while I am there, but at least I can make a note of it for when I get back home.
With all that in mind & that I already have the WS2812 in place / wired up, I guess the next logical step will be learning/experimenting with getting the received commands to simply change the LED colour depending on what button is pressed. I know it would be trivial to have if/then/update_led routine in the main loop. However I believe that later on down the road I will have to use "void functions" to achieve my goal..... which again is all new to me, so I guess that is what I will be looking into next.
Wish me luck!!!!!
Goals for next update
Minimum: Have useful communication between transmitter & receiver to call a function changing colour of an LED depending on the button pressed.
Would be nice: Also Change the receiver to an ESP32, hook up an L298N H-bridge & have motors on/off forward/reverse command functions working to allow further testing.
Bonus: Get all limit switches hooked up so I can start probing the way they trigger at certain times in the motors movements.
