Inspiration
A heart-failure patient may be told to stay under 2,000 mg of sodium a day, but once they get home, that number is hard to use. A bowl of soup could contain 400 mg or 1,400 mg, and taste alone cannot reliably tell the difference.
Most existing tools estimate sodium from labels, barcodes, or food databases. That works for packaged foods, but not for homemade meals, restaurant food, or mixed dishes like soup. We wanted to measure the food itself instead.
A conductivity probe measures dissolved ions, not sodium specifically. That limitation is also useful: low-sodium salt substitutes often replace sodium chloride with potassium chloride, which still produces a strong conductivity signal. For patients with kidney disease or taking certain medications, that can matter because high potassium intake may increase hyperkalemia risk.
A database tells you what a food is supposed to contain. A measurement tells you something about what is actually there.
What it does
The spoon measures salt in liquid food and reports sodium per bite against a daily target set by the patient’s clinician, rather than a generic limit.
Each bite follows a simple sequence: the spoon scoops the food, a load cell measures its weight, and a temperature-conductivity probe takes a reading. When the weight drops, the system registers the bite.
That data feeds three views: a clinician dashboard showing each patient against their personal target, along with sodium sources and blood pressure over time; a patient portal showing how much sodium remains for the day and how much of the current meal fits within that limit; and a live view showing readings from the spoon in real time.
How we built it
Firmware. The ESP32 pulls data from a DFRobot DFR0300 conductivity probe through an ADS1115 16-bit ADC, along with a DS18B20 temperature sensor, an MPU-6050 accelerometer, and an HX711 load cell. Those signals are combined to detect individual bites, which are then sent to the backend over a WebSocket.
Backend. The backend is built with FastAPI and SQLite. It receives bite events, groups them into meals, and starts a new meal whenever there is a gap of more than 20 minutes. It also pushes live updates to a separate channel for each patient and calculates the metrics clinicians care about, such as recent averages, signs of an upward trend, and which foods contributed most of the sodium.
Dashboard. The dashboard is built with React, Vite, and TypeScript.
The whole system is built around one shared contract: a schema that defines the two event types used throughout the project. sample/v2 contains the high-frequency sensor data and stays on the device, while bite/v2 is the permanent record that gets sent through the rest of the system.
Having that shared contract made a huge difference. The firmware, simulator, backend, and dashboard could all be developed against the same interface. For example, we were able to build and test the dashboard using a simulated spoon even while the hardware implementation was still being worked out.
Two pieces of infrastructure ended up being especially useful:
- A mock spoon that runs the same state machine as the real firmware and communicates using the actual protocol over the actual WebSocket connection. This meant dashboard development never had to wait for physical hardware.
- A host-side C++ simulator that runs the real bite-detection code against 400 randomized eating sessions. The simulations vary things like eating speed, spoon height and angle, pauses, hand tremor, IMU error on each axis, load-cell averaging delay, and probe thermal lag. We also test on separate random seeds that were never used during tuning, which gives us a better idea of how the detector behaves under unfamiliar conditions.
Challenges we ran into
We ran into the most challenges actually working on the spoon itself - originally we planned to use a ladle, but the load sensor proved to be unmountable to it. Thus, we opted to use a glass as the vessel for the soup with the load sensor as we didn't have the appropriate hardware to mount it to a normal spoon.
Built With
- react
- typescript
- vite
Log in or sign up for Devpost to join the conversation.