A Node.js proxy server that bridges an oneM2M IoT platform (Mobius / oM2M) and a browser-based monitoring dashboard for a solar + battery + load energy system.
Status: deployed against real solar hardware in October 2019. This repository is preserved as a portfolio snapshot of that deployment — it is not actively maintained.
The wrapper sits in the middle of an IoT data path:
+--------------------+ +--------------------+ +--------------------+
Sensors ---> | oneM2M server | HTTP | Solar-Wrapper | WS | Dashboard (web) |
& inverter | (Mobius / oM2M) | -------> | (this repo) | ------> | |
| | <------- | | <------- | |
+--------------------+ HTTP +--------------------+ WS +--------------------+
PUT commands
- Inbound. oneM2M pushes notification envelopes (
m2m:sgn→m2m:nev→m2m:rep→m2m:fcnt) to/solar,/battery, and/loadwhenever a subscribed value changes. The wrapper unpacks the nested payload and re-emits onesocket.ioevent per measurement to the dashboard. - Outbound. The dashboard POSTs charge / discharge commands to
/chargingand/discharging. The wrapper wraps them in the oneM2Mm2m:fcntformat and PUTs them back into the oneM2M container so the physical controller can act on them.
Single Node.js process exposing two I/O surfaces on two ports:
| Port | Protocol | Direction | Purpose |
|---|---|---|---|
| 19998 | HTTP (Express) | oneM2M → wrapper, dashboard → wrapper | Receive notifications and commands |
| 19997 | Socket.io (WS) | wrapper → dashboard | Push parsed measurements to the browser |
Internally, a single generic worker (getRequestedData.doWork) handles all three device families. Per-family behaviour is expressed as two index-aligned arrays — for example, for solar:
solarEntityName = ["current", "voltage", "power", "daily", "monthly", "annual", "total"];
solarEventName = ["solarCurrent", "solarVoltage", "solarPower", "solarDaily", "solarMonthly", "solarAnnual", "solarTotal"];The worker walks entityName[i], reads that field out of the oneM2M payload, and emits it under eventName[i] on the socket. Adding a measurement is a matter of appending the same index to both arrays. /battery adds extra guards to ignore charge / discharge status and connection-check notifications so that only real measurements are forwarded.
- oneM2M sensors publish values to their respective containers.
- oneM2M notifies subscribers by POSTing the change envelope to Solar-Wrapper.
- The wrapper acknowledges with
200 Successful, then extracts every field named in the entity list. - Each field is emitted as a socket.io event; the dashboard subscribes and re-renders.
- When an operator clicks a control on the dashboard, the dashboard POSTs
{ "command": "0" | "1" }to/chargingor/discharging. - The wrapper PUTs the equivalent
m2m:fcntpayload to the oneM2M container, and the physical controller reacts.
Every step is mirrored to ./log/logging.log via log4js and to stdout with a millisecond-precision timestamp.
oneM2M is a standards-body IoT protocol used heavily in Korean research deployments (Mobius is KETI's reference implementation of it). Its notification envelopes are deeply nested (m2m:sgn.m2m:nev.m2m:rep.m2m:fcnt.<field>) and are not something a typical web dashboard wants to parse directly. This wrapper is the thin adapter that lets a plain socket.io dashboard talk to a oneM2M backend without either side having to know about the other's shape — the dashboard sees named measurement events, and the oneM2M server sees a well-behaved subscriber and command source.
node server_express.jsNote: the
npm startscript inpackage.jsonpoints atserver.js, which does not exist in this snapshot — invoke the file directly.
Configuration lives in config/app_config.js. In this snapshot only port is actually read at runtime; the target oneM2M URL for command PUTs is hard-coded to the LAN address of the 2019 test bench (http://192.168.0.21:8080/~/in-cse/fcnt-548319540). The log/ directory must exist for log4js to initialise.
- Express 4 — HTTP surface
- socket.io 2 — push channel to the dashboard
- request / axios — outbound oneM2M PUTs
- log4js — file + console logging
- body-parser, cors — middleware
server_express.js Main entry point — HTTP + socket.io server
server_express2.js Earlier partial draft, kept for history — not used
config/app_config.js Port, Mobius URLs, oneM2M header values
log/ log4js output (gitignored)