nodegraph and nodes defined and receiveable from web interface packet

master
Jake 2025-10-15 00:39:54 +08:00
parent 9e3979f4d6
commit 29758d8abf
4 changed files with 224 additions and 8 deletions

View File

@ -2,6 +2,7 @@
#include <base64.h> #include <base64.h>
#include "feetech.h" #include "feetech.h"
#include "animation.h" #include "animation.h"
#include "nodegraph.h"
#define DEVICE_NAME "Little Sophia" #define DEVICE_NAME "Little Sophia"
#define FIRMWARE_VERSION "0.0.1" #define FIRMWARE_VERSION "0.0.1"
@ -101,6 +102,7 @@ const uint16_t testPayloadLength = sizeof(testPayload);
void setup() { void setup() {
Serial.begin(1000000); Serial.begin(1000000);
Serial.setRxBufferSize(1024);
for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) {
Serial.println(i); Serial.println(i);
delay(500); delay(500);
@ -137,11 +139,11 @@ void setup() {
// anim.saveToFile("/scurve.anim"); // anim.saveToFile("/scurve.anim");
// Serial.println("SAVED"); // Serial.println("SAVED");
//handleSaveFile(testPayload, testPayloadLength); //handleSaveFile(testPayload, testPayloadLength);
Serial.println("Loading test.anim"); Serial.println("Loading test.anim");
anim.loadFromFile("/test.anim"); anim.loadFromFile("/test.anim");
Serial.println(anim.header.frameCount); Serial.println(anim.header.frameCount);
anim.printCurves(); anim.printCurves();
// Serial.print("CurveSegment size: "); // Serial.print("CurveSegment size: ");
// Serial.println(sizeof(CurveSegment)); // Serial.println(sizeof(CurveSegment));
//anim.createBasicSCurve(); //anim.createBasicSCurve();
@ -682,6 +684,25 @@ bool parseAnimationPayload(const uint8_t* payload, uint16_t length, Animation& a
ptr += sizeof(CurveSegment); ptr += sizeof(CurveSegment);
} }
// ✅ Advance ptr to node graph payload
//ptr += curveCount * sizeof(CurveSegment);
// 🔹 Parse node graph
uint8_t nodeCount = ptr[0];
Serial.print("Node count: ");
Serial.println(nodeCount);
sendMessage(String("Node count: ") + String(nodeCount));
sendMessage(String("ptr offset: ") + String(ptr - payload));
uint16_t remainingLength = length - (ptr - payload);
if (remainingLength > 0) {
loadNodeGraph(ptr, remainingLength, animation.nodeGraph);
} else {
Serial.println("No node graph data found");
}
sendMessage(printNodeGraph(animation.nodeGraph));
// 🔹 Save using received filename // 🔹 Save using received filename
animation.saveToFile(("/" + String(filename)).c_str()); animation.saveToFile(("/" + String(filename)).c_str());

View File

@ -1,10 +1,10 @@
#ifndef ANIMATION_FILE_H #pragma once
#define ANIMATION_FILE_H
#include <Arduino.h> #include <Arduino.h>
#include "FS.h" #include "FS.h"
#include "FFat.h" #include "FFat.h"
#include <unordered_map> #include <unordered_map>
#include "nodegraph.h"
#define NUM_CHANNELS 5 #define NUM_CHANNELS 5
@ -60,6 +60,8 @@ public:
void createBasicSCurve(); void createBasicSCurve();
void createEaseOutCurve(); void createEaseOutCurve();
AnimationHeader header; AnimationHeader header;
NodeGraph nodeGraph;
private: private:
//uint16_t data[MAX_FRAMES][NUM_CHANNELS]; //uint16_t data[MAX_FRAMES][NUM_CHANNELS];
@ -67,4 +69,3 @@ private:
}; };
#endif

137
nodegraph.cpp Normal file
View File

@ -0,0 +1,137 @@
#include "nodegraph.h"
#include <cstring>
// CurveNode evaluation
void CurveNode::evaluate(uint32_t tick) {
//outputValue = getCurveValue(curveID, tick);
}
// ServoNode evaluation
void ServoNode::evaluate(uint32_t tick) {
//setMotorPWM(motorID, inputValue);
}
// NodeGraph tick
void NodeGraph::tick(uint32_t currentTick) {
// First pass: evaluate all nodes
for (Node* node : nodes) {
node->evaluate(currentTick);
}
// Optional: if you want to simulate wiring between nodes,
// you could add a second pass here to propagate values
}
void loadNodeGraph(const uint8_t* packet, size_t length, NodeGraph& graph) {
size_t offset = 0;
// Read node count
uint16_t nodeCount = packet[offset];
offset += 1;
// Parse nodes
for (uint16_t i = 0; i < nodeCount; ++i) {
if (offset + 6 > length) break; // safety check
uint8_t type = packet[offset++];
uint8_t id = packet[offset++];
uint16_t x = packet[offset] | (packet[offset + 1] << 8); offset += 2;
uint16_t y = packet[offset] | (packet[offset + 1] << 8); offset += 2;
Node* node = nullptr;
switch (type) {
case TYPE_CURVENODE: { // CurveNode
if (offset + 1 > length) break;
uint8_t curveID = packet[offset++];
auto* curve = new CurveNode();
curve->id = id;
curve->type = type;
curve->x = x;
curve->y = y;
curve->curveID = curveID;
node = curve;
break;
}
case TYPE_SERVONODE: { // ServoNode
if (offset + 1 > length) break;
uint8_t motorID = packet[offset++];
auto* servo = new ServoNode();
servo->id = id;
servo->type = type;
servo->x = x;
servo->y = y;
servo->motorID = motorID;
node = servo;
break;
}
case TYPE_NOISENODE: { // NoiseNode
if (offset + 17 > length) break;
offset += 17; // skip for now
break;
}
default:
break;
}
if (node) {
graph.nodes.push_back(node);
}
}
// Parse connections
if (offset + 2 > length) return;
uint16_t connectionCount = packet[offset];
offset += 1;
for (uint16_t i = 0; i < connectionCount; ++i) {
if (offset + 2 > length) break;
uint8_t fromID = packet[offset++];
uint8_t toID = packet[offset++];
graph.connections.push_back({fromID, toID});
}
}
String printNodeGraph(const NodeGraph& graph) {
String output = "📦 NodeGraph Dump\n";
output += "Nodes:\n";
for (const Node* node : graph.nodes) {
output += " ID " + String(node->id);
output += " | Type " + String(node->type);
output += " | Pos (" + String(node->x) + ", " + String(node->y) + ")";
switch (node->type) {
case TYPE_CURVENODE: { // CurveNode
const CurveNode* curve = static_cast<const CurveNode*>(node);
output += " | CurveID " + String(curve->curveID);
break;
}
case TYPE_SERVONODE: { // ServoNode
const ServoNode* servo = static_cast<const ServoNode*>(node);
output += " | MotorID " + String(servo->motorID);
break;
}
// case 2: { // NoiseNode
// const NoiseNode* noise = static_cast<const NoiseNode*>(node);
// output += " | Noise a=" + String(noise->a, 2);
// output += " b=" + String(noise->b, 2);
// output += " c=" + String(noise->c, 2);
// output += " d=" + String(noise->d, 2);
// output += " seed=" + String(noise->seed);
// break;
// }
}
output += "\n";
}
output += "Connections:\n";
for (const auto& conn : graph.connections) {
output += " " + String(conn.fromID) + "" + String(conn.toID) + "\n";
}
return output;
}

57
nodegraph.h Normal file
View File

@ -0,0 +1,57 @@
#pragma once
#include <vector>
#include <cstdint>
#include <Arduino.h>
#define TYPE_NODE 0x01
#define TYPE_SERVONODE 0x02
#define TYPE_CURVENODE 0x03
#define TYPE_NOISENODE 0x04
// Base Node class
struct Node {
uint8_t id;
uint8_t type;
uint16_t x;
uint16_t y;
virtual void evaluate(uint32_t tick) = 0;
virtual ~Node() {}
};
// CurveNode: evaluates a curve at a given tick
struct CurveNode : public Node {
uint8_t curveID;
uint16_t outputValue;
void evaluate(uint32_t tick) override;
};
// ServoNode: sends a value to a motor
struct ServoNode : public Node {
uint8_t motorID;
uint16_t inputValue;
void evaluate(uint32_t tick) override;
};
// NodeGraph container
struct NodeConnection {
uint8_t fromID;
uint8_t toID;
};
class NodeGraph {
public:
std::vector<Node*> nodes;
std::vector<NodeConnection> connections;
void tick(uint32_t currentTick);
};
// Loader function
void loadNodeGraph(const uint8_t* packet, size_t length, NodeGraph& graph);
String printNodeGraph(const NodeGraph& graph);