How to send integers via popen? - cocoa

I need to be able to send an integer from cocoa to an arduino.
It is easy to send characters, i.e. single digit integers, but I can't seem to find a way of sending two and three digit integers.
The purpose of this is to control the brightness of an LED continuously from 0 to 255.
So far, I can either turn it on and off using the following code:
int ledPin = 9; // LED connected to digital pin 9
int incomingByte = 0; // for incoming serial data
void setup() {
// initialize the digital pin as an output:
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
}
void loop()
{
if (Serial.available() > 0) {
// read the incoming byte:
incomingByte = Serial.read();
if(incomingByte == 105){ //105 corresponds to i and is programmed in cocoa to turn the LED on
digitalWrite(ledPin, HIGH);
}
else if(incomingByte == 111){ //111 corresponds to o and is programmed in cocoa to turn the LED on
digitalWrite(ledPin, LOW);
}
}
}
However, I can't work out how to set values between 0 and 255. Instead of 'digitalWrite', I would use 'AnalogWrite', however, I don't know how to send the incomingByte to be a value between 0 and 255.
This is the cocoa code:
#import "MainController.h"
#implementation MainController
-(IBAction)ledOn:(id)sender{
popen("echo i > /dev/cu.usbmodem1411", "r");
}
-(IBAction)ledOff:(id)sender{
popen("echo o > /dev/cu.usbmodem1411", "r");
}
#end
Thanks.

You can treat the integers with more than one digits as cstring in your arduino code then convert them to integer via atoi().
The following code will capture the bytes from the serial buffer as cstring:
char buffer[MAX_BUFFER_SIZE]; // global c character array variable
boolean inputReady()
{
byte index = 0;
byte avail = Serial.available();
if(avail > 0)
{
while(index < avail)
{
byte val;
do
{
val = Serial.read();
}
while (val == -1); // if value is no longer -1, bytes are captured
buffer[index] = val;
index++;
}
buffer[index] = 0; //terminate the character array
return true;
}
return false;
}
Note: I prefer cstrings than the String class built in the arduino IDE since it uses more memory, but be careful using cstrings as they are prone to memory leaks if not managed well.
This is how your loop() block should look like:
void loop()
{
if(inputReady())
{
int pwmValue = atoi(buffer); // convert ascii buffer to integer.
if(pwmValue >= 0 && pwmValue <= 255) // filter values from 0-255
analogWrite(ledPin, pwmValue);
else
digitalWrite(ledPin, LOW); // turn off if pwmValue is more than 255
}
delay(100);
}
You can then send pwm values as string from your cocoa code via popen().
#import "MainController.h"
#implementation MainController
-(IBAction)ledPWMValue:(id)sender{
popen("echo 254 > /dev/cu.usbmodem1411", "r");
}
#end
I've tested this on my Arduino Uno and will probably work on other variants. I hope this helps you in your project and best of luck!

Related

Image displaying using Arduino- Processing connection and ADXL345 sensor

I am working on an artistic project that includes an ADXL345 sensor (accelerometer), Arduino Uno R3 Board, Arduino IDE 2.0.3 and Processing 4.1.2.
I want Processing to display images randomly and continuously every time the values of the sensor that are received from the serial communication with the Arduino sketch, go x>5, x<-5, y.5, y.-5, z>1, z<-1.
UPDATE:
A friend helped me with some lines of code and now the image being displayed when I move the sensor.
CHALLENGE:
What I want to be able to do now is run the processing sketch ONCE and let the windows containing the images pop up, close down, open new windows and display new random images from my folder. For this process to repeat on itself so I don't have to run the sketch manually every time.
These are the codes that I am using in Arudino and Processing.
ARDUINO
void setup() {
// initialize serial communication at 9600 baud rate
Serial.begin(9600);
}
void loop() {
// send x, y, and z values over serial
int x = analogRead(A0);
int y = analogRead(A1);
int z = digitalRead(2);
Serial.print(x);
Serial.print(",");
Serial.print(y);
Serial.print(",");
Serial.println(z);
delay(1000);
}
& PROCESSING
import processing.serial.*;
Serial mySerial;
PImage fragment;
int rand;
void setup() {
size(1000, 500);
rand = int(random(0,133));
takerandomimage("C:/Users/user/Documents/Processing/Trial_300123/frag_" + nf(rand, 3) + ".jpg");
String portName = Serial.list()[0];
mySerial = new Serial(this, portName, 9600);
println("Serial port connected: " + portName);
loop();
}
void takerandomimage(String fn) {
fragment = loadImage(fn);
println(fragment);
}
void draw() {
background(255); //clears the screen
if (fragment.width>0 && fragment.height > 0){ //check if image has been loaded
String data = mySerial.readStringUntil('\n');
if (data != null && data != "\n" && data != " " && data != "\r" && data != "\t") {
println("Data received: " + data);
String[] values = data.split(" ",0);
int counter = 0;
int x = 0;
int y = 0;
int z = 0;
for(String w :values){
System.out.println(w);
if (counter == 1)
x = int(w);
if ( counter == 4)
y = int(w);
if ( counter == 7)
z = int(w);
counter++;
}
println(x);
println(y);
println(z);
if (x < 0 || y > 0 || z > 0) {
takerandomimage("C:/Users/user/Documents/Processing/Trial_300123/frag_" + nf(rand, 3) + ".jpg");
image(fragment, 0,0);
delay(1000);
}
}
}
}
Thank you!!
So many things are right with your code (and question)(+1):
your question mentions version of the Arduino/Processing used, Arduino board, sensor and also includes minimal code focused on the issue
checking if data is not null
checking if the split array is right length, etc.
You are so close !
There is one issue throwing off parsing:
your Processing code assumes you have the x, y, z values one line: String data = mySerial.readStringUntil('\n');
your Arduino code is printling mulitple lines instead of one(e.g. Serial.println(x); instead of Serial.print(x))
to double check, you would see x, y, z values (as well as the two "," symbols) in Serial Monitor on separate lines each (instead of all on one line)
I suspect the intention was to use println() only on the last line:
void setup() {
// initialize serial communication at 9600 baud rate
Serial.begin(9600);
}
void loop() {
// send x, y, and z values over serial
int x = analogRead(A0);
int y = analogRead(A1);
int z = digitalRead(2);
Serial.print(x);
Serial.print(",");
Serial.print(y);
Serial.print(",");
Serial.println(z);
delay(1000);
}
(The above should print 3 values separated by "," on a single line. Double check if z should be a digitalRead(2) (returning 1 or 0) reading on pin 2 on your Arduino or analogRead(A2) (returning 0 to 1023) reading analog pin A2.)
Update
Another idea is move the accelerometer conditions in the Arduino code and simply output a single byte/character ('i' for example) when you want to trigger loading a random image in Processing.
Here's a rough / untested example:
void setup() {
// initialize serial communication at 9600 baud rate
Serial.begin(9600);
}
void loop() {
// send x, y, and z values over serial
int x = analogRead(A0);
int y = analogRead(A1);
int z = digitalRead(2);
// if the right motion is picked up, send 'i' to Processing to load a random image
if (x > 5 || y > 5 || z > 0) {
Serial.print('i');
}
delay(1000);
}
Note that with the 1000 ms (1s) delay means you can't trigger an image change faster than that. Adjust the delay as needed.
Additionally you say you want to trigger the change when accelerometer values meet these conditions:
x>5, x<-5, y.5, y.-5, z>1, z<-1.
If so, you might want to change:
if (x > 5 || y > 5 || z > 0)
to
if (abs(x) > 5 || abs(y) > 5 || abs(z) > 0)
(where abs() returns the absolute value (e.g. 5 remains 5, but -5 becomes 5))
Hopefully the values you selected make sense for the motion you're trying to capture with the accelerometer (otherwise the Serial Plot tool in Arduino can be helpful).
If you move the condition to Arduino it simplifies the Processing code as you don't need to wait for a full string to parse into an array: you can read just one character.
import processing.serial.*;
Serial mySerial;
PImage fragment;
void setup() {
size(1000, 500);
loadRandomImage();
String portName = Serial.list()[0];
mySerial = new Serial(this, portName, 9600);
}
void loadRandomImage() {
int rand = int(random(0,133));
String filename = "frag_" + nf(rand, 3) + ".jpg"
fragment = loadImage(fn);
}
void draw() {
// if there is a serial data
if(mySerial.available > 0){
// read one byte/char (mask (&) because byte range is -127 to 127 in java, but 0 to 255 in c++)
char data = mySerial.read() & 255;
// optional: print serial to debug
println("received from Arduino", data);
// if the expect char (i) was received, load random image
if(data == 'i'){
loadRandomImage();
}
}
background(255); //clears the screen
if (fragment.width>0){ //check if image has been loaded
image(fragment, 0, 0);
}
}
Notice a few other minor changes:
I've renamed takerandomimage to loadRandomImage() and changed it so each time the function gets called it generates the random number and loads the random image
whenever the 'i' character is received from Ardiuno a new random is loaded
whatever random image has been set gets displayed continuously (decoupling image display from reading serial data). Before you only loaded and displayed an image if the right serial data came through).

How to read a frame from hm01b0 image sensor?

I am attempting to connect the Arducam HM01B0 sensor to the nRF52840-DK embedded board using bare metal programming. The sensor has pins for a four-bit serial interface, and to use it, the user has to solder the pins. However, I am trying to make the HM01B0 work with the one-bit interface and capture an image.
I am able to read and write to the sensor registers over I2C. The datasheet presents the following timings characteristics to capture an image over a serial line.
I have connected the logic analyzer and I am able to see the corresponding waveform that is the data is being streamed over D0 line.
For 1 bit interface,the image sensor has a frame rate of 30 FPS and the frequency of PCLK is 36 Mhz. I know that GPIO pins would not be able to read the input signals in this
frequency range but I don't know how else to read a serial line. One option is making the image sensor as SPIM master and nrf52840 as SPIM slave. The image sensor provides the clock over PCLK which can be configured as SPI clock and the line D0 can be configured as MOSI line. However to read valid data bits/bytes over the serial line, VSYNC and HSYNC should also be read and made sure that their value is according to the serial video interface timings diagram. So I tried to read the serial line D0 by configuring it as a GPIO pin and I read 0x00 always.
How can I configure the image sensor and capture an image?
This is the code I have written,
// array to hold a complete frame
uint8_t frameBuffer[324][324];
#define PIN_VSYNC (28)
#define PIN_HSYNC (29)
#define PIN_PCLK (30)
#define PIN_D0 (31)
// initialise the pins of sensor
void hm0360_init()
{
// GPIO I/P
nrf_gpio_cfg_input(PIN_VSYNC, NRF_GPIO_PIN_PULLUP);
nrf_gpio_cfg_input(PIN_HSYNC, NRF_GPIO_PIN_PULLUP);
nrf_gpio_cfg_input(PIN_PCLK, NRF_GPIO_PIN_PULLUP);
// GPIO O/P
nrf_gpio_cfg_input(PIN_D0,NRF_GPIO_PIN_PULLUP);
for(int i = 0; i < 324 ; i++){
for(int j = 0; j < 324 ; j++){
frameBuffer[i][j] = 0;
}
}
}
void hm01b0_read_frame_bits()
{
int VSYNC_count = -1, HREF_count = 0;
int incomingBits;
unsigned char byte = 0;
int byteArrayIndex = 0;
int bitCount = 0;
while(nrf_gpio_pin_read(PIN_VSYNC) == LOW) { }
while(nrf_gpio_pin_read(PIN_VSYNC) == HIGH)
{
while(nrf_gpio_pin_read(PIN_HSYNC) == false && nrf_gpio_pin_read(PIN_VSYNC) == HIGH) { }
VSYNC_count++;
HREF_count = 0;
//Read a row
while(nrf_gpio_pin_read(PIN_HSYNC) == true)
{
while(nrf_gpio_pin_read(PIN_PCLK) == false) { }
// read 0 always
incomingBits = nrf_gpio_pin_read(PIN_D0);
byte = (byte << 1) | incomingBits;
bitCount++;
if (bitCount == 8) {
frameBuffer[VSYNC_count][HREF_count++] = byte;
bitCount = 0;
byte = 0;
}
if (VSYNC_count == 324) {
break;
}
while(nrf_gpio_pin_read(PIN_PCLK) == true) { }
}
}
}

Turn MIDI control input into virtual keystrokes (or virtual USB buttons etc) on macOS

I would like to use MIDI control devices (like this https://www.korg.com/us/products/computergear/nanokontrol2/ ) to generate control input for various software, Blender in particular.
One way is obviously to add MIDI input handling into Blender. Adding low-level code to Blender to listen for MIDI buttons and sliders is not hard at all, and I have basically implemented that. (I.e. I added a new "class" of input, MIDI, at Blender's lowest level.) But connecting that to the existing keyboard and mouse plumbing and especially UI functionality to associate functions with input is much more complex, and not something I want to dive into now.
Another way would perhaps be to instead run some separate software that listens for MIDI events and turns those into virtual keystrokes. Assuming it is possible to generate a much larger variety of keystrokes than there are actual keys on any keyboard, this could work nicely (like, generate keystrokes corresponding to various Unicode blocks that no real keyboard ever has). Does this sound feasible? Is a11y APIs what I should be looking at to implement such virtual keystroke generation? This way would have the benefit that it would work with any software.
Or does anybody have some better idea?
OK, so I wrote this small program. Works beautifully (once you give it the right to generate key events in System Preferences > Security & Privacy > Privacy > Accessibility > Allow the apps below to control your computer). MIDI note on and off events and MIDI controller value changes generate macOS key presses of keys with CJK Unified Ideographs as the characters.
But, then I see that Blender is the kind of software that thinks that ASCII should be enough for everybody. In other words, Blender has hardcoded restrictions that the only keys it handles are basically those on an English keyboard. You can't bind even Cyrillic or Greek keys (for which there after all exists actual keyboards) to Blender functions, much less CJK keys. Sigh. Back to the drawing board.
/* -*- Mode: ObjC; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; fill-column: 150 -*- */
#import <array>
#import <cassert>
#import <cstdio>
#import <Foundation/Foundation.h>
#import <CoreGraphics/CoreGraphics.h>
#import <CoreMidi/CoreMidi.h>
static constexpr bool DEBUG_MIDI2KBD(true);
static constexpr int BASE_KEYCODE(1000);
static CGEventSourceRef eventSource;
static std::array<unsigned char, 16*128> control;
static void NotifyProc(const MIDINotification *message, void *refCon)
{
}
static void sendKeyDownOrUpEvent(int character, int velocity, bool down) {
CGEventRef event = CGEventCreateKeyboardEvent(eventSource, character + BASE_KEYCODE, down);
// We send CJK Unified Ideographs characters
constexpr int START = 0x4E00;
assert(character >= 0 && character <= 20989);
const UniChar string[1] = { (UniChar)(START + character) };
CGEventKeyboardSetUnicodeString(event, 1, string);
CGEventPost(kCGAnnotatedSessionEventTap, event);
}
int main(int argc, const char * argv[]) {
#autoreleasepool {
MIDIClientRef midi_client;
OSStatus status = MIDIClientCreate((__bridge CFStringRef)#"MIDI2Kbd", NotifyProc, nullptr, &midi_client);
if (status != noErr) {
fprintf(stderr, "Error %d while setting up handlers\n", status);
return 1;
}
eventSource = CGEventSourceCreate(kCGEventSourceStatePrivate);
control.fill(0xFF);
ItemCount number_sources = MIDIGetNumberOfSources();
for (int i = 0; i < number_sources; i++) {
MIDIEndpointRef source = MIDIGetSource(i);
MIDIPortRef port;
status = MIDIInputPortCreateWithProtocol(midi_client,
(__bridge CFStringRef)[NSString stringWithFormat:#"MIDI2Kbd input %d", i],
kMIDIProtocol_1_0,
&port,
^(const MIDIEventList *evtlist, void *srcConnRefCon) {
const MIDIEventPacket* packet = &evtlist->packet[0];
for (int i = 0; i < evtlist->numPackets; i++) {
// We expect just MIDI 1.0 packets.
// The words are in big-endian format.
assert(packet->wordCount == 1);
const unsigned char *bytes = reinterpret_cast<const unsigned char *>(&packet->words[0]);
assert(bytes[3] == 0x20);
if (DEBUG_MIDI2KBD)
printf("Event: %02X %02X %02X\n", bytes[2], bytes[1], bytes[0]);
switch ((bytes[2] & 0xF0) >> 4) {
case 0x9: // Note-On
assert(bytes[1] <= 0x7F);
sendKeyDownOrUpEvent((bytes[2] & 0x0F) * 128 + bytes[1], bytes[0], true);
break;
case 0x8: // Note-Off
assert(bytes[1] <= 0x7F);
sendKeyDownOrUpEvent((bytes[2] & 0x0F) * 128 + bytes[1], bytes[0], false);
break;
case 0xB: // Control Change
assert(bytes[1] <= 0x7F);
const int number = (bytes[2] & 0x0F) * 128 + bytes[1];
if (control.at(number) != 0xFF) {
int diff = bytes[0] - control.at(number);
// If it switches from 0 to 127 or back, we assume it is not really a continuous controller but
// a button.
if (diff == 127)
diff = 1;
else if (diff == -127)
diff = -1;
if (diff > 0) {
for (int i = 0; i < diff; i++) {
// Send keys indicating single-step control value increase
sendKeyDownOrUpEvent(16*128 + number * 2, diff, true);
sendKeyDownOrUpEvent(16*128 + number * 2, diff, false);
}
} else if (diff < 0) {
for (int i = 0; i < -diff; i++) {
// Send key indicating single-step control value decrease
sendKeyDownOrUpEvent(16*128 + number * 2 + 1, -diff, true);
sendKeyDownOrUpEvent(16*128 + number * 2 + 1, -diff, false);
}
}
}
control.at(number) = bytes[0];
break;
}
packet = MIDIEventPacketNext(packet);
}
});
if (status != noErr) {
fprintf(stderr, "Error %d while setting up port\n", status);
return 1;
}
status = MIDIPortConnectSource(port, source, nullptr);
if (status != noErr) {
fprintf(stderr, "Error %d while connecting port to source\n", status);
return 1;
}
}
CFRunLoopRun();
}
return 0;
}

How to read multiple ADC channels using PIC24; can only get AN0

I am using a PIC24 to read data using 3 analog inputs but am only getting 1 to show the right result. I looked everywhere on the internet and am still not able to get the code to work.
I am trying to read 3 analog input signals and am only able to read in AN0.
I am using an accelerometer to obtain the data and show it on the LCD screen for now. I was able to implement 3 different ways to take the data and display it but only an0 works and an1 is not the right value.
void InitADC(int amask) {
AD1PCFG = 0xFFF8; // select AN0, AN1, AN2 as analog inputs
AD1CON1 = 0x00E0; // auto convert # end of sampling, Integer Data out.
// see Text pg. 179 & Sec. 17 on AD1CON1.
//AD1CON2bits.CSCNA = 1;
AD1CON3 = 0x1F01; // Tad = 2xTcy = 125ns. 31*Tad for conversion time.
//AD1CSSL = 0xFFF7; // Scan 3 channels
AD1CON1bits.ADON = 1; // Turn on the ADC
} // InitADC
main() {
InitADC(0xFFF8); // initialize the ADC and analog inputs
char x_string [12];
char y_string [12];
char z_string [12];
//TRISB = 1; // all PORTB pins as outputs
TRISBbits.TRISB0 = 1;
TRISBbits.TRISB1 = 1;
TRISBbits.TRISB2 = 1;
InitPMP(); // Initialize the Parallel Master Port
InitLCD(); // Initialize the LCD
float x_val;
float y_val;
float z_val;
float x_axis, y_axis, z_axis;
while (1) // main loop
{
x_axis= SelectPort(0);
x_val= ((((x_axis * 3.3)/ 1024)-1.58)/0.380);
sprintf(x_string, "X: %0.2f ", x_val);
ms_delay(2.5);
y_axis= SelectPort(1);
y_val= ((((y_axis * 3.3)/ 1024)-1.58)/0.380);
sprintf(y_string, "Y: %0.2f ", y_val);
ms_delay(2.5);
z_axis= SelectPort(2);
z_val= ((((z_axis * 3.3)/ 1024)-1.58)/0.380);
sprintf(z_string, "Z: %0.2f ", z_val);
ms_delay(2.5);
}
Here is the code where the data is read:
int SelectPort(int ch)
{
//int *result;
AD1CON1bits.ADON = 0; // Turn off the ADC to reconfigure
//result = &ADC1BUF0;
switch(ch) // set values based on the channel to use
{
case 0: // select AN0 as analog input
//AD1CHSbits.CH0SA=0;
//result = ADC1BUF0;
AD1PCFG = 0xFFFE;
break;
case 1:
//AD1CHSbits.CH0SA=1;
//result = ADC1BUF1;
AD1PCFG = 0xFFFD; // select AN1 as analog input
break;
case 2:
//AD1CHSbits.CH0SA=2;
AD1PCFG = 0xFFFB; // select AN2 as analog input
break;
// there's only so many options here, so there's not really a default case
}
AD1CON1bits.ADON = 1; // Turn on the ADC
AD1CHS = ch; // 1. select analog input channel
AD1CON1bits.SAMP = 1; // 2. Start sampling.
while (!AD1CON1bits.DONE); //5. wait for conversion to complete
AD1CON1bits.DONE = 0; // 6. clear flag. We are responsible see text.
return ADC1BUF0; // 7. read the conversion results
}
I am new to PIC24 and need help to figure out why I am not able to get multiple ADC channels to read the data.
You should always give some time for the ADC input voltage to settle after changing its input port. Also, make sure the impedance of your inputs signals are less than 10K.
Separating the ADC channel switching and reading its value will help, and give you an opportunity to add a delay.
I could not find AD1PCFG in the PIC24 datasheet. AD1PCFG is a PIC32 register... PIC24 uses ANSx and TRSx to set pins as analog inputs. Analog inputs should be set once, during the boot sequence. Feeding an analog signal to a digital input CMOS pin will result in increased power draw, and can even lead to hardware failure!
// Set the pins as analog inputs once and for all in your setup code.
void InitADC()
{
// select RB0/AN0, RB1/AN1, RB2/AN2 as analog inputs
TRISB |= 0x07;
ANSB |= 0x07;
// ...
}
void ADC_SelectInput(int ch)
{
AD1CHS = ch & 0x0F
}
int ADC_Read()
{
AD1CON1bits.SAMP = 1;
while (!AD1CON1bits.DONE)
;
AD1CON1bits.DONE = 0;
return ADC1BUF0;
}
Your loop then becomes:
// ...
int adc_in;
while (1)
{
ADC_SelectInput(0);
ms_delay(3); // settling delay, you could also do something
// else in the meantime instead of waiting.
// NOTE: the argument to ms_delay() is an integer
// so waiting at least 2.5 ms makes it 3.
// when computing floats, use float constants, not double.
x_val = (((ADC_Read() * 3.3f) / 1024) - 1.58f) / 0.380f;
sprintf(x_string, "X: %0.2f ", x_val);
ADC_SelectInput(1);
ms_delay(3);
y_val = (((ADC_Read() * 3.3f) / 1024) - 1.58f) / 0.380f;
sprintf(y_string, "Y: %0.2f ", y_val);
ADC_SelectInput(2);
ms_delay(3);
z_val = (((ADC_Read() * 3.3f) / 1024) - 1.58f) / 0.380f;
sprintf(z_string, "Z: %0.2f ", z_val);
}

how use the MPU 6050 in ultra low power mode

I'm currently trying to set up a fermentation specific gravity monitor, using a tilt sensor. The process can take several weeks, and must be contained in a sterile container, so must be battery powerered. I'm using a slightly modified ESP8266-01, which enters sleep mode then wakes once an hour to take a measurement, transmit the data, and return to sleep mode. I'm using an MPU6050 for the tilt sensor. Firstly, I can't seem to put the mpu into sleep mode when the esp is off, it always seems to take around 4mA, and secondly, I only need one axis, is it possible to disable everything else to limit power consumption further? I can't seem to find anything in the manual to disable axis, only to calibrate them. my code is below
experimenting with the registers below seem to make no difference, adding them, taking them out altogether, still takes around 4mA. Tried setting to 1 to put the mpu to sleep at the end of the cycle but makes no difference.
Wire.write(0x6B);
Wire.write(0);
I'm very new to this and im struggling to interpret the manual when it refers to bit6 in addr 6b, how do i set bit 6?
If i could restict the mpu to only 1 axis, no acceleration, and to deep sleep inbetween measurements I should be able to get the power consumption around 0.5mA which gives me agood battery life using a single 18650. Any advice would be greatly appreciated!
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include "MPU6050.h"
#include "I2Cdev.h"
#include "Wire.h"
// Update these with values suitable for your network.
const char* ssid = "****";
const char* password = "******";
IPAddress server(192, 168, 1, 90);
WiFiClient espClient5;
PubSubClient client(espClient5);
long lastMsg = 0;
char msg[50];
const uint8_t scl = 5; //D1
const uint8_t sda = 4; //D2
int val;
int prevVal = 0;
String pubString;
char gravity[50];
MPU6050 mpu;
const int sleepTimeS = 10; //only 10 seconds for testing purposes, set to
1hr when operational
int counter=0;
int16_t ax, ay, az;
int16_t gx, gy, gz;
void setup_wifi() {
delay(10);
// We start by connecting to a WiFi network
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
randomSeed(micros());
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
void callback(char* topic, byte* payload, unsigned int length) { //not
required in this application
}
void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
// Create a random client ID
String clientId = "test";
clientId += String(random(0xffff), HEX);
// Attempt to connect
if (client.connect(clientId.c_str())) {
Serial.println("connected");
// Once connected, publish an announcement...
client.publish("AliveRegister", "FermentMon");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}
#define ONE_WIRE_BUS 2 // D4 on physical board
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature DS18B20(&oneWire);
float prevTemp = 0;
void setup() {
counter = 0;
Serial.begin(9600);
Wire.begin(0,2);
Wire.write(0x6B); //PWR_MGMT_1 register
Wire.write(0); // set to zero wakes teh 6050
Wire.endTransmission(true);
delay(100);
setup_wifi();
client.setServer(server, 1883);
client.setCallback(callback);
if (!client.connected()) {
reconnect();
}
Serial.println("Initialize MPU");
mpu.initialize();
Serial.println(mpu.testConnection() ? "Connected" : "Connection failed");
float temp;
DS18B20.requestTemperatures();
temp = DS18B20.getTempCByIndex(0); // first temperature sensor
char buff[100];
dtostrf(temp, 0, 2, buff);
temp = temp + 0.5;
int tRound = int(temp);
client.publish("Fermenter/temperature", buff);
Serial.print("Fermenter Temperature: ");
Serial.println(temp);
prevTemp = tRound;
mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
val = map(ax, -17000, 17000, 0, 180);
pubString = String(val);
pubString.toCharArray(gravity, pubString.length() + 1);
client.publish("Fermenter/angle", gravity);
Serial.print("Gravity angle: ");
Serial.println(val);
delay(500);
// counter = counter+1;
Serial.println("sleep mode");
Wire.write(0x6B); //PWR_MGMT_1 register
Wire.write(1); // set to zero wakes teh 6050
// sleep
ESP.deepSleep(sleepTimeS * 1000000);
delay(2000);
}
void loop() {
client.loop();
}
I'm very new to this and im struggling to interpret the manual when it refers to bit6 in addr 6b, how do i set bit 6?
Setting a bit is simple.
Use the follow functions to avoid any brain storming.
// Write register bit
void writeRegisterBit(uint8_t reg, uint8_t pos, bool state)
{
uint8_t value;
value = readRegister8(reg);
if (state)
{
value |= (1 << pos);
}
else
{
value &= ~(1 << pos);
}
writeRegister8(reg, value);
}
// Write 8-bit to register
void writeRegister8(uint8_t reg, uint8_t value)
{
Wire.beginTransmission(MPU_addr);
#if ARDUINO >= 100
Wire.write(reg);
Wire.write(value);
#else
Wire.send(reg);
Wire.send(value);
#endif
Wire.endTransmission();
}
Example Usage: writeRegisterBit(MPU6050_REG_INT_PIN_CFG, 5, 1); //Register 37;Interrupt Latch Enable
For your application:
void acclSetSleepEnabled(bool state)
{
writeRegisterBit(MPU6050_REG_PWR_MGMT_1, 6, state);
}
If i could restict the mpu to only 1 axis, no acceleration, and to deep sleep inbetween measurements I should be able to get the power consumption around 0.5mA which gives me agood battery life using a single 18650
To enter low power accelerometer mode use the following function:
void lowPowerAccel(uint8_t frequency) {
uint8_t value;
value = readRegister8(MPU6050_REG_PWR_MGMT_2);
value &= 0b00111000;
value |= (frequency << 6) | 0b111;
writeRegister8(MPU6050_REG_PWR_MGMT_2, value);
value = readRegister8(MPU6050_REG_PWR_MGMT_1);
value &= 0b10010111;
value |= 0b00111000;
writeRegister8(MPU6050_REG_PWR_MGMT_1, value);
}
This lowPowerAccel function also puts the gyro to standy mode. The function needs a wake up frequency parameter.
This is defined as follows:
/*
* LP_WAKE_CTRL | Wake-up Frequency
* -------------+------------------
* 0 | 1.25 Hz
* 1 | 2.5 Hz
* 2 | 5 Hz
* 3 | 10 H
*/
#define LP_WAKE_CTRL_1_25 0x00
#define LP_WAKE_CTRL_2_5 0x01
#define LP_WAKE_CTRL_5 0x02
#define LP_WAKE_CTRL_10 0x03
I hope, I could answer some of your questions.
Good luck! :)
Are you using a breakout board for the MPU6050? e.g. GY-521. Often they use linear regulators and leds which will consume additional power. It may be necessary to remove these and run the IMU from a direct power source.
Each register in the MPU6050 is 8 bits wide. When setting an individual bit to a desired value you can either use bitwise manipulation (not practical here as we aren't directly interacting with the registers) or directly set all of the bits in the register to the register's new state e.g. 0b00100000 ~ 0x20. Instead of writing a 1 to 0x6B when attempting to put the MPU6050 to sleep you should be writing 0x20.
https://www.invensense.com/wp-content/uploads/2015/02/MPU-6000-Register-Map1.pdf
Referencing page 40-42, if you want to take things a step further you can disable the temperature sensor, accelerometers, and redundant gyroscope axes to save power while the device is active.

Resources