I purchased a LattePanda using Bitcoin.


The Bitcoin part is unremarkable – I don’t speculate on it nor use it as a store of value, just for transactions. Buying via a maker web site was simple (their shopping cart software is Shopify and they use BitPay as a payment cartridge). But what really intrigued me was the Lattepanda itself.

It’s pretty cool.

You may know that most single-board computers (SBCs) run on ARM chips. ARM cornered the market for low-powered devices and most mobile phones use them. So too most SBCs including the well known Raspberry Pi. My collection of Beaglebones, RasPis, Odroids and others is now joined by a SBC powered by an Intel. Harking back to an earlier post, this is mostly brought on by a couple of things: a need to run some trenchant Windows software. I’d also like to play movies through it, if possible.

That second need is brought on by never really getting my Plex project working: I could get the recording part through my HD Homerun TV recorder working, and storage wasn’t a problem – it was just that the particular box I chose to put it on wasn’t strong enough to do the transcoding needed to play back the movies on different devices. Perhaps also the Lattepanda won’t be sufficient as I notice that the Atom chip runs REALLY hot and I’m just fitting it with a fan to take some heat away. Let’s see if that helps, but I may need to invest in an Odroid XU4 or something to really get the power boost my home media systems require.

For now I am gearing up the Windows platform on the Lattepanda and installing all the necessary updates, then putting it in a case and under my aquarium where it will run the Seneye Connect software and possibly a webcam on my fishes.

Reading a Seneye using a Raspberry Pi – conclusion

So it can be done, I’ve now able to read a Seneye USB device using my own server and Python code. You can read about the first couple of steps here and here. The process was difficult due to a number of quirks and barriers:

  • the Seneye code uses a C++ STRUCT for data mapping, implying byte-alignment for different data types and bit padding
  • the SUD holds local data readings until it is able to reconnect to the cloud, and will fill up to capacity if it is not connected to the cloud
  • values are decimal-shifted for display
  • if any errors occur the device seems to enter a timeout-locked state (perhaps by missing the BYESUD message?) and has to be unplugged

Firstly the official Seneye C++ code as compiled on my machine and reading the reference mug of water:


Then the output from the Seneye code, without their lovely ASCII logo art:

  Device: LSDF0982LSDFOSDLJKS9E89S0D9SDMF v.2.0.16  Type:  Home
  Temperature (C)   │ 20.375                      Is Kelvin           │                   ┌────┘ └────┐
  pH                │ 7.94                        Kelvin              │                   │ ┌─┐   ┌─┐ │
  NH3 (ppm)         │ 0.02                        PAR                 │                   │ └─┘   └─┘ │
  In Water          │ True                        LUX                 │                   │           │
  Slide NOT fitted  │ False                       PUR                 │                   │   ┌───┐   │
  Slide Expired     │ False                                           │                   │   │   │   │
  Press R for reading, 1-5 to change LED, Q to quit

Taking this and observing that the pH value was 7.94, which is 031a in hex, I  scanned through the printed hex dump (and I really must write a small routine to dump binary, hex, and offset in bytes-per-line).  The latest output from my code with debugging turned on can be seen below.

('device       >>>', )
('configuration>>>', )
('interface    >>>', )
('endpoint in  >>>', )
('endpoint out >>>', )
('HELO ret code>>>', 8)
('HELO hex     >>>', (<type 'array.array'>, 64, '88:01:01:01:30:4e:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00'))
('READ ret code>>>', 7)
('sensor hex   >>>', (<type 'array.array'>, 64, '00:01:57:59:59:59:05:00:00:00:1a:03:10:00:1a:4f:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00'))
('sensor bits len>', 512)
('sensor bits  >>>', '00000000000000010101011101011001010110010101100100000101000000000000000000000000000110100000001100010000000000000001101001001111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000')
('BYE ret code >>>', 6)
{'InWater': True,
 'NH3': 0,
 'SlideExpired': False,
 'SlideNotFitted': False,
 'Temp': 20,
 'pH': 7}

This means that the pH starts at position 80, runs for two bytes, and is little endian (so x031a is the equivalent of 794 in decimal, and packed into two bytes as it is a short means it looks like ‘1a.03’. The 7.94 comes about because certain of the values returned are divided by 1000, certain ones by 100. It helps to read and understand the Seneye C++ code.

There is obviously some extra commands that are sent by the Seneye Connect software, plus I believe some cryptographic hashes in operation to ensure devices update, only upload from authorised accounts, and other things to keep the Seneye ecosystem together.


This means that it is unlikely that home DIYers will be able to replicate the full Seneye Connect experience. A LattePanda running Windows 10 and the Seneye Connect software or Seneye SWS probably still give the best experience, along with SMS text alerts and the Seneye dashboard. However, for those who are willing to tinker with code this project provides a reasonable solution.