element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products and Partners
    Products and Partners
    • Arduino
    • Avnet & Tria Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • About Us
    About the element14 Community
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Japan
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      •  Vietnam
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
Smart Home & Healthcare Challenge
  • Challenges & Projects
  • Design Challenges
  • Smart Home & Healthcare Challenge
  • More
  • Cancel
Smart Home & Healthcare Challenge
Forum Palno & Slemon #2.2 Configuring and Validating
  • News
  • Projects
  • Forum
  • Details
  • Leaderboard
  • Files
  • Members
  • DC
  • More
  • Cancel
  • New
Join Smart Home & Healthcare Challenge to participate - click to join for free!
Actions
  • Share
  • More
  • Cancel
Forum Thread Details
  • Replies 0 replies
  • Subscribers 17 subscribers
  • Views 45 views
  • Users 0 members are here
  • esp32
  • smart home & healthcare
Related

Palno & Slemon #2.2 Configuring and Validating

Qbit
Qbit 14 hours ago

Hey this is part 2.2 for my projects Palno and Slemon together they both combine make up an accurate sleep tracker and with the power of UNO Q lets you wake up at the right time so you don`t feel tired and exhausted anymore, 

you can read the past posts from here:

#1 Introduction

#2.1 Configuring and Validating

In this I will be configuring Arduino UNO Q and  HLK-LD2410B   the mm wave sensor.

1) Arduino UNO Q

It comes pre-installed with debian image you don`t have to do anything at first but later you can update if you want and no cmd is required simply a message will pop-up asking you to update to the latest version, I think there is some sort of bug, sometimes when I had already installed the latest version the  install the latest version message pop up but it when I click to continue to just take away time and gives that the latest version is already installed.  (If you have updated it make sure if you are also using the arduino IDE for the sketch that you have installed the latest version of the board also if you don`t , somethings will not work as expected.)

Thank you again to Element14 for awarding me the Arduino UNO Q and selecting me as the winner of the Top Tech voices.

image

As you power it up it will show nice boot animation on the led matrix then heart.

You can use it as the standalone computer with a  USB-C dongle or  connect via usb(c) to your computer and use it. 

If you are using it with computer as I am, Download the App Labs here.

First you connect it to the computer then it will show  in the app labs (be patient it could take up to 40s for the Q to boot up.)

image 

Then click on it.

image

give it a name then 

image

Setup wifi connection so you flash it  remotely.

image

Setup a password that will be used when you are working with the terminal interface if you forget it you can change it in setting.

also then it will ask for  a restart , after you do it then it will again ask to name it but that doesn`t work.(see below),  

Play this video

So unlpug the board then replug it again then it will come to the home page.

2) HLK LD2410B mmWave Sensor:

The radar module we are using for the motion tracking while sleep.

image

Its pinout is this:

image

Thanks to shabaz!

I will not be using the OUT pin because it doesn`t give that much information only occupied/not occupied.

This was the most challenging firstly I was not able to find anything usable other then the post from shabaz did some help you can see here. But I wanted more data then that so I scrolled a lot through datasheets and finally get some some useful datasheets not completely the source code but enough information about how does it function and what does it give in uart.

This is the code I developed, I had also used AI because it could take me weeks to build.

/
 *   1  State        (0=none 1=moving 2=static 3=both)
 *   2  MovDist_cm
 *   3  MovEnergy
 *   4  StaDist_cm
 *   5  DetDist_cm
 *   6  MG0  (gate 0 = 0.00–0.75 m)
 *   7  MG1  (gate 1 = 0.75–1.50 m)
 *   8  MG2
 *   9  MG3
 *   10 MG4
 *   11 MG5
 *   12 MG6
 *   13 MG7
 *   14 MG8
 *   15 SG3  (static gate 3 onwards — SG0/1/2 removed)
 *   16 SG4
 *   17 SG5
 *   18 SG6
 *   19 SG7
 *   20 SG8
 *   21 Light  (photosensitive 0–255)
 *   22 OutPin (0=empty 1=occupied)
 *

 */

// ──────────────────────── Config ────────────────────────────
#define LD2410_BAUD      256000   // Change to 115200 for LD2410S/D
#define DBG_BAUD         115200
#define RX_PIN           16
#define TX_PIN           17
#define PLOT_MS          50       // 20 Hz output — fast like Arduino plotter
#define MAX_GATES        9
#define FRAME_BUF_SIZE   128

// ──────────────────── Protocol bytes ────────────────────────
#define DH0 0xF4
#define DH1 0xF3
#define DH2 0xF2
#define DH3 0xF1
#define DF0 0xF8
#define DF1 0xF7
#define DF2 0xF6
#define DF3 0xF5

#define CH0 0xFD
#define CH1 0xFC
#define CH2 0xFB
#define CH3 0xFA
#define CF0 0x04
#define CF1 0x03
#define CF2 0x02
#define CF3 0x01

#define CMD_EN_CFG   0x00FF
#define CMD_END_CFG  0x00FE
#define CMD_EN_ENG   0x0062
#define CMD_CL_ENG   0x0063
#define CMD_RD_VER   0x00A0
#define CMD_RD_PRM   0x0061
#define CMD_SET_DIST 0x0060
#define CMD_SET_SENS 0x0064
#define CMD_RESET    0x00A2
#define CMD_RESTART  0x00A3

// ──────────────────── Global variables ──────────────────────
static uint8_t  fb[FRAME_BUF_SIZE];
static uint16_t fbi  = 0;
static uint16_t fexp = 0;
static uint16_t fcnt = 0;
static uint8_t  hi   = 0;
static uint8_t  hb[4];
static bool     isDataFrame = false;

enum { ST_HDR, ST_LEN, ST_DATA, ST_FOOT } rxSt = ST_HDR;

static bool     newData    = false;
static bool     streaming  = false;  // ← starts false; 'n' enables
static bool     csvMode    = false;

// Sensor readings — raw, no ceiling applied
static uint8_t  g_state   = 0;
static uint16_t g_movDist = 0;   // cm
static uint16_t g_movEn   = 0;   // energy (sensor outputs 0-100)
static uint16_t g_staDist = 0;   // cm
static uint16_t g_detDist = 0;   // cm
static uint8_t  g_maxMov  = 8;
static uint8_t  g_maxSta  = 8;
static uint16_t g_movG[MAX_GATES];  // per-gate motion energy
static uint16_t g_staG[MAX_GATES];  // per-gate static energy
static uint16_t g_light   = 0;
static uint8_t  g_out     = 0;

static unsigned long lastPlot = 0;

// ──────────────────── Command helpers ────────────────────────

static void sendCmd(uint16_t word, const uint8_t* dat, uint8_t dlen) {
  uint16_t ilen = 2 + dlen;
  uint8_t buf[32];
  uint8_t i = 0;
  buf[i++]=CH0; buf[i++]=CH1; buf[i++]=CH2; buf[i++]=CH3;
  buf[i++]=ilen&0xFF; buf[i++]=ilen>>8;
  buf[i++]=word&0xFF; buf[i++]=word>>8;
  for (uint8_t j=0; j<dlen && j<20; j++) buf[i++]=dat[j];
  buf[i++]=CF0; buf[i++]=CF1; buf[i++]=CF2; buf[i++]=CF3;
  Serial2.write(buf, i);
  Serial2.flush();
}

static void enCfg()  { uint8_t v[]={0x01,0x00}; sendCmd(CMD_EN_CFG, v, 2); delay(80); }
static void endCfg() { sendCmd(CMD_END_CFG, NULL, 0); delay(80); }

static void cmdEnEng()  { enCfg(); sendCmd(CMD_EN_ENG, NULL,0); delay(80); endCfg(); }
static void cmdVer()    { enCfg(); sendCmd(CMD_RD_VER, NULL,0); delay(150); endCfg(); }
static void cmdParam()  { enCfg(); sendCmd(CMD_RD_PRM, NULL,0); delay(150); endCfg(); }
static void cmdReset()  { enCfg(); sendCmd(CMD_RESET,  NULL,0); delay(150); endCfg(); }
static void cmdRestart(){ enCfg(); sendCmd(CMD_RESTART,NULL,0); delay(150); }

static void cmdSetDist(uint8_t mg, uint8_t sg, uint16_t t) {
  uint8_t v[18]={
    0x00,0x00,mg,0,0,0,
    0x01,0x00,sg,0,0,0,
    0x02,0x00,(uint8_t)(t&0xFF),(uint8_t)(t>>8),0,0
  };
  enCfg(); sendCmd(CMD_SET_DIST, v, 18); delay(80); endCfg();
}

static void cmdSetSens(uint8_t m, uint8_t s) {
  uint8_t v[18]={
    0x00,0x00,0xFF,0xFF,0,0,
    0x01,0x00,m,0,0,0,
    0x02,0x00,s,0,0,0
  };
  enCfg(); sendCmd(CMD_SET_SENS, v, 18); delay(80); endCfg();
}

// ──────────────────── Frame parser ───────────────────────────

static bool match4(const uint8_t* a, uint8_t b0,uint8_t b1,uint8_t b2,uint8_t b3){
  return a[0]==b0 && a[1]==b1 && a[2]==b2 && a[3]==b3;
}

static void parseData() {
  uint16_t ilen = fb[4] | (fb[5]<<8);
  if (ilen < 12) return;
  uint16_t fp = 6 + ilen;
  if (fp+4 > fbi) return;
  if (!match4(fb+fp, DF0,DF1,DF2,DF3)) return;

  const uint8_t* d = fb+6;
  uint8_t dtype = d[0];
  if (d[1] != 0xAA) return;

  g_state   = d[2];
  g_movDist = (uint16_t)d[3]|((uint16_t)d[4]<<8);
  g_movEn   = d[5];
  g_staDist = (uint16_t)d[6]|((uint16_t)d[7]<<8);
  // d[8] = staEnergy — intentionally NOT stored (column 5 removed)
  g_detDist = (uint16_t)d[9]|((uint16_t)d[10]<<8);

  if (dtype == 0x01 && ilen >= 20) {
    g_maxMov = d[11];
    g_maxSta = d[12];
    if (g_maxMov >= MAX_GATES) g_maxMov = MAX_GATES-1;
    if (g_maxSta >= MAX_GATES) g_maxSta = MAX_GATES-1;

    uint8_t idx = 13;
    for (uint8_t i=0; i<=g_maxMov; i++)
      g_movG[i] = (idx < ilen+6) ? d[idx++] : 0;
    for (uint8_t i=g_maxMov+1; i<MAX_GATES; i++) g_movG[i]=0;

    for (uint8_t i=0; i<=g_maxSta; i++)
      g_staG[i] = (idx < ilen+6) ? d[idx++] : 0;
    for (uint8_t i=g_maxSta+1; i<MAX_GATES; i++) g_staG[i]=0;

    g_light = (idx < ilen+6) ? d[idx++] : 0;
    g_out   = (idx < ilen+6) ? d[idx]   : 0;
  }

  newData = true;
}

static void parseAck() {
  uint16_t ilen = fb[4]|(fb[5]<<8);
  if (ilen < 4) return;
  uint16_t fp = 6+ilen;
  if (fp+4 > fbi || !match4(fb+fp,CF0,CF1,CF2,CF3)) return;
  uint16_t ack = fb[6]|(fb[7]<<8);
  uint16_t st  = fb[8]|(fb[9]<<8);
  if (ack==0x01A0 && st==0 && ilen>=10)
    Serial.printf("# FW type=%d V%d.%02X\n",fb[10],fb[11],fb[14]);
  if (ack==0x0161 && st==0 && ilen>=30) {
    Serial.printf("# Params: maxMov=%d maxSta=%d timeout=%ds\n",
                  fb[11],fb[12], fb[31]|(fb[32]<<8));
  }
}

// ──────────────────── UART state machine ─────────────────────

static void readRadar() {
  while (Serial2.available()) {
    uint8_t b = Serial2.read();
    switch (rxSt) {
      case ST_HDR:
        hb[hi++]=b;
        if (hi<4) break;
        if      (match4(hb,DH0,DH1,DH2,DH3)){isDataFrame=true; memcpy(fb,hb,4);fbi=4;fcnt=0;rxSt=ST_LEN;}
        else if (match4(hb,CH0,CH1,CH2,CH3)){isDataFrame=false;memcpy(fb,hb,4);fbi=4;fcnt=0;rxSt=ST_LEN;}
        else { hb[0]=hb[1];hb[1]=hb[2];hb[2]=hb[3];hi=3; }
        break;
      case ST_LEN:
        if (fbi<FRAME_BUF_SIZE) fb[fbi++]=b; fcnt++;
        if (fcnt>=2) {
          fexp=fb[4]|((uint16_t)fb[5]<<8);
          if (fexp==0||fexp>FRAME_BUF_SIZE-12){rxSt=ST_HDR;hi=0;fbi=0;fcnt=0;}
          else {fcnt=0;rxSt=ST_DATA;}
        }
        break;
      case ST_DATA:
        if (fbi<FRAME_BUF_SIZE) fb[fbi++]=b; fcnt++;
        if (fcnt>=fexp){fcnt=0;rxSt=ST_FOOT;}
        break;
      case ST_FOOT:
        if (fbi<FRAME_BUF_SIZE) fb[fbi++]=b; fcnt++;
        if (fcnt>=4){
          if (isDataFrame) parseData(); else parseAck();
          rxSt=ST_HDR;hi=0;fbi=0;fcnt=0;
        }
        break;
    }
  }
}

// ──────────────────── Output ─────────────────────────────────

static void printHeader() {
  char sep = csvMode ? ',' : '\t';
  // Engineering mode columns (22 total — cols 5/16/17/18 removed)
  Serial.print(F("State"));     Serial.print(sep);
  Serial.print(F("MovDist_cm")); Serial.print(sep);
  Serial.print(F("MovEnergy")); Serial.print(sep);
  Serial.print(F("StaDist_cm")); Serial.print(sep);
  Serial.print(F("DetDist_cm")); Serial.print(sep);
  // Moving gate energies MG0–MG8
  for (uint8_t i=0;i<MAX_GATES;i++){
    Serial.print(F("MG")); Serial.print(i); Serial.print(sep);
  }
  // Static gate energies SG3–SG8 only (SG0,SG1,SG2 removed)
  for (uint8_t i=3;i<MAX_GATES;i++){
    Serial.print(F("SG")); Serial.print(i); Serial.print(sep);
  }
  Serial.print(F("Light")); Serial.print(sep);
  Serial.println(F("OutPin"));
}

static void outputLine() {
  char sep = csvMode ? ',' : '\t';

  Serial.print(g_state);   Serial.print(sep);
  Serial.print(g_movDist); Serial.print(sep);
  Serial.print(g_movEn);   Serial.print(sep);
  Serial.print(g_staDist); Serial.print(sep);
  // col 5 (StaEnergy) skipped
  Serial.print(g_detDist); Serial.print(sep);

  // MG0–MG8
  for (uint8_t i=0;i<MAX_GATES;i++){
    Serial.print(g_movG[i]); Serial.print(sep);
  }
  // SG3–SG8 only (SG0,SG1,SG2 skipped)
  for (uint8_t i=3;i<MAX_GATES;i++){
    Serial.print(g_staG[i]); Serial.print(sep);
  }
  Serial.print(g_light); Serial.print(sep);
  Serial.println(g_out);
}

// ──────────────────── Command handler ────────────────────────

static void printHelp() {
  Serial.println(F("# ────────────────────────────────────────────────"));
  Serial.println(F("# LD2410 Plotter Commands"));
  Serial.println(F("# STREAM: n=Start streaming  x=Stop streaming"));
  Serial.println(F("# FMT:    t=Tab(Plotter/Python)  c=CSV"));
  Serial.println(F("# INFO:   H=Header  v=Firmware  p=Params"));
  Serial.println(F("# SENS:   1=5  2=15  3=30  4=50(def)  5=75"));
  Serial.println(F("# CFG:    d=MaxDist8/5s  r=FactoryReset  R=Restart"));
  Serial.println(F("# ────────────────────────────────────────────────"));
}

static void handleCmd() {
  if (!Serial.available()) return;
  char c = Serial.read();
  while (Serial.available()) Serial.read();

  switch (c) {
    case 'n':
      // Start streaming: enable engineering mode then begin output
      if (!streaming) {
        Serial.println(F("# Enabling engineering mode..."));
        cmdEnEng();
        delay(200);
        readRadar(); // drain ACKs
        streaming = true;
        printHeader();
        Serial.println(F("# STREAMING START"));
      }
      break;

    case 'x':
      streaming = false;
      Serial.println(F("# STREAMING STOP — send 'n' to resume"));
      break;

    case 't': csvMode=false;     Serial.println(F("# Tab mode")); break;
    case 'c': csvMode=true;      Serial.println(F("# CSV mode")); break;
    case 'H': printHeader(); break;
    case 'v': cmdVer();   Serial.println(F("# FW query sent")); break;
    case 'p': cmdParam(); Serial.println(F("# Params query sent")); break;
    case '1': cmdSetSens( 5, 5); Serial.println(F("# Sens=5/5")); break;
    case '2': cmdSetSens(15,15); Serial.println(F("# Sens=15/15")); break;
    case '3': cmdSetSens(30,30); Serial.println(F("# Sens=30/30")); break;
    case '4': cmdSetSens(50,50); Serial.println(F("# Sens=50/50")); break;
    case '5': cmdSetSens(75,75); Serial.println(F("# Sens=75/75")); break;
    case 'd': cmdSetDist(8,8,5); Serial.println(F("# MaxDist=8/8 5s")); break;
    case 'r': cmdReset();   Serial.println(F("# Factory reset")); break;
    case 'R': cmdRestart(); Serial.println(F("# Restart sent")); break;
    case 'h': case '?': printHelp(); break;
    default:  Serial.printf("# Unknown '%c' — h=help\n",c); break;
  }
}

// ──────────────────── Setup & Loop ───────────────────────────

void setup() {
  Serial.begin(DBG_BAUD);
  while (!Serial) delay(5);

  memset(g_movG, 0, sizeof(g_movG));
  memset(g_staG, 0, sizeof(g_staG));

  Serial.println(F("# LD2410 Raw Plotter booting..."));
  Serial.printf("# Radar baud=%d | Plot interval=%dms\n", LD2410_BAUD, PLOT_MS);

  Serial2.begin(LD2410_BAUD, SERIAL_8N1, RX_PIN, TX_PIN);
  delay(1500);

  cmdVer();   delay(200);
  cmdParam(); delay(200);
  readRadar(); // drain ACK responses

  Serial.println(F("# Ready. Send 'n' to start streaming, 'h' for help."));
}

void loop() {
  // Always read radar data from UART (keeps buffer clear even when not streaming)
  readRadar();

  // Handle user/Python commands
  handleCmd();

  // Only output data when streaming is active
  if (streaming && newData && (millis()-lastPlot >= PLOT_MS)) {
    lastPlot = millis();
    newData  = false;
    outputLine();
  }
}

To extract reliable telemetry without dropping bytes, the firmware processes incoming UART data using a non-blocking finite state machine (ST_HDR, ST_LEN  ST_DATA  ST_FOOT). It continuously inspects the serial stream for the radar's signature 4-byte headers—0xF4 0xF3 0xF2 0xF1 for active data frames and 0xFD 0xFC 0xFB 0xFA for command acknowledgments. Once a valid payload is buffered and verified against its frame footer, parseData() unpacks state flags, target distances, total motion energy, and individual per-gate thresholds directly into global variables.

For easier host synchronization, data streaming remains paused on boot until an 'n' command is sent over the Serial Monitor or through a companion Python script. Once triggered, the sketch streams 22 columns of tab-separated or CSV values at 20 Hz (every 50 ms). This output rate is fast enough for smooth real-time plotting while keeping serial overhead low enough to prevent lag during long sleep-monitoring sessions.

You can also adjust parameters interactively without reflashing the board. Simple single-character keyboard inputs let you tweak distance gate sensitivities (1 through 5), query active firmware parameters, or trigger hardware resets on the fly. This setup makes it simple to capture clean signal data for the Arduino Serial Plotter or stream it into external Python scripts for further sleep-stage analysis.

This the testing data I captured:

You don't have permission to edit metadata of this video.
Edit media
x
image
Upload Preview
image

In the next part  I will collect data to train the models and understand it to make the suitable algorithm.

Thank you for reading !


  • Sign in to reply
  • Cancel
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2026 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube